Agent 与 Skills 之间的区别通过一个简单图书馆借阅系统案例实践指南

Agent 与 Skills 之间的区别通过一个简单图书馆借阅系统案例实践指南

摘要

在技术语境下,两者的关系可以简单概括和区分为:Agent 是决策中心,Skills 是执行函数。

  • Agent (智能体): 一个具备推理(Reasoning)、规划(Planning)和记忆(Memory)能力的实体。它通过 ReAct(Reasoning and Acting)框架决定何时调用哪个 Skill。

  • Skills (技能/工具): 预定义的、具有明确输入/输出模式的原子化功能(API、数据库查询、本地脚本等)。

在人工智能技术快速发展的今天,Agent(智能代理)与 Skills(技能)的关系架构已成为构建复杂AI应用的核心范式。
LangChain作为当前最流行的开源框架,提供了预构建的agent架构和丰富的工具集成,让开发者能够快速构建适应性强的AI代理系统。
本文将深入探讨Agent与Skills的关系架构,基于Debian 12系统环境,从基础原理到实战部署一个图书馆借阅系统的简单案例来加深理解,提供完整的技术入门指南。

一、Agent与Skills架构演进史

1.1 概念起源与版本演进

Agent-Skills架构的发展经历了多个重要阶段:

  • 2020-2021年:早期概念阶段,主要关注基础LLM集成
  • 2022年:LangChain 0.0.1发布,首次提出Agent-Tools架构
  • 2023年:LangChain 0.1.0正式版,Skills概念正式确立
  • 2024年:LangChain 0.2.0,引入分层Skills管理和动态加载机制
  • 2025年:当前版本0.3.0,支持多模态Skills和分布式Agent协同

1.2 关键特性演变

v0.1.x 特性

  • 基础Agent执行框架
  • 简单Tools集成
  • 串行任务处理

v0.2.x 特性

  • Skills模块化设计
  • 动态技能加载
  • 记忆管理优化
  • 支持LangGraph工作流

v0.3.x 特性(当前)

  • 多Agent协同架构
  • 技能市场(Skills Marketplace)
  • 实时性能监控
  • 安全沙箱机制
  • 跨语言Skills支持

二、Debian 12环境搭建与原理剖析

2.1 系统环境准备

1
2
3
4
5
6
7
8
9
10
11
12
13
# 更新系统源
sudo apt update && sudo apt upgrade -y

# 安装基础依赖
sudo apt install -y python3 python3-pip python3-venv git curl wget \
build-essential libpq-dev libssl-dev postgresql-client

# 创建项目目录
mkdir ~/agent-skills-project && cd ~/agent-skills-project

# 创建虚拟环境
python3 -m venv venv
source venv/bin/activate

2.2 核心架构原理

Agent与Skills的关系架构基于分层设计模式,包含三个核心层次:

  1. Agent层:负责决策制定、任务规划和协调
  2. Skills层:提供具体功能实现,封装业务逻辑
  3. Integration层:连接外部系统和数据源
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 架构示意图
class Agent:
def __init__(self, skills_registry):
self.skills = skills_registry # 技能注册中心

def execute_task(self, task):
# 1. 任务分析
skill_needed = self.analyze_task(task)

# 2. 技能选择
selected_skill = self.select_skill(skill_needed)

# 3. 技能执行
result = selected_skill.execute(task)

# 4. 结果处理
return self.process_result(result)

2.3 关键特性详解

2.3.1 动态技能加载

Skills可以在运行时动态加载和卸载,无需重启Agent服务,极大提升了系统的灵活性和可维护性。

2.3.2 技能组合机制

多个Skills可以组合形成复合技能,支持复杂的业务场景处理。

2.3.3 记忆管理

内置的对话记忆和上下文管理机制,确保Skills在执行过程中能够保持状态连续性。

三、详细配置指南(带中文注释)

3.1 核心配置文件 agent_config.yaml

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
# Agent核心配置文件
agent:
# Agent唯一标识符
id: "library_management_agent"

# Agent名称(显示用)
name: "图书馆管理智能代理"

# Agent描述信息
description: "负责图书馆借阅、归还、查询等核心业务的智能代理"

# LLM模型配置
llm:
# 模型提供商(openai, anthropic, local等)
provider: "openai"

# 具体模型名称
model_name: "gpt-4-turbo"

# API密钥(环境变量引用)
api_key: "${OPENAI_API_KEY}"

# 温度参数(控制随机性,0-1)
temperature: 0.3

# 最大token数限制
max_tokens: 2000

# 记忆管理配置
memory:
# 记忆类型(conversation, vector, hybrid)
type: "hybrid"

# 对话历史保存条数
history_size: 10

# 向量存储配置
vector_store:
# 向量数据库类型
type: "chroma"

# 持久化路径
persist_path: "./data/vector_store"

# 嵌入模型
embedding_model: "text-embedding-ada-002"

# 技能注册中心配置
skills_registry:
# 技能目录路径
skills_dir: "./skills"

# 自动加载新技能
auto_load: true

# 技能缓存时间(分钟)
cache_ttl: 30

# 技能执行超时时间(秒)
execution_timeout: 60

# 安全配置
security:
# 沙箱模式(true/false)
sandbox_mode: true

# 允许的外部调用域名
allowed_domains:
- "localhost"
- "127.0.0.1"
- "postgres"

# 技能权限控制
skill_permissions:
# 读取权限
read: ["query_book", "check_availability"]
# 写入权限
write: ["borrow_book", "return_book", "add_book"]

# 日志配置
logging:
# 日志级别(debug, info, warning, error, critical)
level: "info"

# 日志文件路径
file_path: "./logs/agent.log"

# 是否输出到控制台
console_output: true

# 数据库连接配置
database:
# 数据库类型
type: "postgresql"

# 连接主机
host: "localhost"

# 端口
port: 5432

# 数据库名称
database: "library_db"

# 用户名
user: "library_user"

# 密码(环境变量)
password: "${DB_PASSWORD}"

# 连接池大小
max_connections: 10

# 连接超时时间(秒)
connection_timeout: 30

# 环境变量配置
environment:
# 开发/测试/生产环境
env: "development"

# 调试模式
debug: true

# 性能监控
monitoring:
enabled: true
metrics_port: 9090

3.2 技能配置文件示例 skills/borrow_book.yaml

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
# 技能配置文件
skill:
# 技能唯一标识符
id: "borrow_book"

# 技能名称
name: "图书借阅技能"

# 技能描述
description: "处理用户借阅图书的请求,验证用户资格和图书可用性"

# 技能版本
version: "1.0.0"

# 技能作者
author: "library-team"

# 技能分类
category: "library_operations"

# 依赖的Skills
dependencies:
- "check_user_eligibility"
- "check_book_availability"

# 输入参数定义
input_schema:
type: "object"
properties:
user_id:
type: "string"
description: "用户ID"
required: true
book_id:
type: "string"
description: "图书ID"
required: true
borrow_date:
type: "string"
format: "date"
description: "借阅日期,格式YYYY-MM-DD"
default: "current_date"

# 输出参数定义
output_schema:
type: "object"
properties:
success:
type: "boolean"
description: "操作是否成功"
transaction_id:
type: "string"
description: "交易ID"
due_date:
type: "string"
format: "date"
description: "应还日期"
message:
type: "string"
description: "操作结果消息"

# 执行逻辑配置
execution:
# 执行类型(python, javascript, shell等)
type: "python"

# 入口函数
entry_point: "borrow_book.execute"

# 超时时间(秒)
timeout: 30

# 重试次数
max_retries: 3

# 重试间隔(秒)
retry_interval: 2

# 资源限制
resources:
# 内存限制(MB)
memory_limit: 256

# CPU限制(核数)
cpu_limit: 0.5

# 网络访问权限
network_access: false

# 错误处理
error_handling:
# 自定义错误映射
error_codes:
USER_NOT_FOUND: "用户不存在"
BOOK_NOT_AVAILABLE: "图书不可借阅"
MAX_BORROWS_REACHED: "已达到最大借阅数量"

# 默认错误处理函数
default_handler: "error_handlers.default_handler"

# 缓存配置
cache:
# 是否启用缓存
enabled: true

# 缓存时间(秒)
ttl: 60

# 缓存键生成策略
key_strategy: "user_book_combination"

# 监控指标
metrics:
# 启用性能监控
enable_performance: true

# 自定义指标
custom_metrics:
- "borrow_success_rate"
- "average_processing_time"

四、部署架构与最佳实践

4.1 生产环境部署架构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
┌─────────────────────────────────────────────────────────────┐
│ 负载均衡层 │
│ Nginx/HAProxy │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│ 应用服务层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Agent-1 │ │ Agent-2 │ │ Agent-3 │ │
│ │ (Primary) │ │ (Backup) │ │ (Read-only) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│ 数据存储层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ PostgreSQL │ │ Vector │ │ Redis │ │
│ │ (主从复制) │ │ DB (Chroma)│ │ (缓存) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘

4.2 容器化部署(Docker Compose)

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
version: '3.8'

services:
# Agent主服务
agent-service:
build:
context: .
dockerfile: Dockerfile.agent
container_name: library-agent
ports:
- "8080:8080"
- "9090:9090" # 监控端口
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- DB_PASSWORD=${DB_PASSWORD}
- ENVIRONMENT=production
volumes:
- ./skills:/app/skills
- ./data:/app/data
- ./logs:/app/logs
depends_on:
- postgres
- redis
- vector-db
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
interval: 30s
timeout: 10s
retries: 3

# PostgreSQL数据库
postgres:
image: postgres:15-alpine
container_name: library-postgres
environment:
- POSTGRES_DB=library_db
- POSTGRES_USER=library_user
- POSTGRES_PASSWORD=${DB_PASSWORD}
volumes:
- postgres_data:/var/lib/postgresql/data
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
ports:
- "5432:5432"
restart: unless-stopped

# Redis缓存
redis:
image: redis:7-alpine
container_name: library-redis
ports:
- "6379:6379"
volumes:
- redis_data:/data
restart: unless-stopped

# 向量数据库
vector-db:
image: chromadb/chroma:latest
container_name: library-vector-db
ports:
- "8000:8000"
volumes:
- vector_data:/chroma
restart: unless-stopped

# 监控服务
prometheus:
image: prom/prometheus:latest
container_name: agent-prometheus
ports:
- "9091:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
depends_on:
- agent-service

volumes:
postgres_data:
redis_data:
vector_data:

4.3 关键注意事项

⚠️ 重要注意事项:

  1. 安全隔离:生产环境中必须启用沙箱模式,限制Skills的网络访问权限,防止恶意代码执行。

  2. 性能优化:Skills的执行超时时间应根据业务场景合理设置,避免阻塞Agent主线程。

  3. 版本控制:Skills必须严格遵循语义化版本控制,确保向后兼容性。

  4. 监控告警:必须配置完整的监控指标,包括技能执行成功率、响应时间、资源使用率等。

  5. 灾备策略:Agent服务应部署多实例,实现故障自动切换,确保服务高可用。

五、实战Demo:图书馆借书管理系统

5.1 数据库设计(PostgreSQL)

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
-- 图书表
CREATE TABLE books (
id VARCHAR(50) PRIMARY KEY,
title VARCHAR(255) NOT NULL,
author VARCHAR(100) NOT NULL,
isbn VARCHAR(20) UNIQUE,
category VARCHAR(50),
total_copies INTEGER DEFAULT 1,
available_copies INTEGER DEFAULT 1,
published_year INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 用户表
CREATE TABLE users (
id VARCHAR(50) PRIMARY KEY,
name VARCHAR(100) NOT NULL,
email VARCHAR(100) UNIQUE NOT NULL,
phone VARCHAR(20),
max_borrow_limit INTEGER DEFAULT 5,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 借阅记录表
CREATE TABLE borrow_records (
id SERIAL PRIMARY KEY,
user_id VARCHAR(50) REFERENCES users(id),
book_id VARCHAR(50) REFERENCES books(id),
borrow_date DATE NOT NULL,
due_date DATE NOT NULL,
return_date DATE,
status VARCHAR(20) DEFAULT 'borrowed' CHECK (status IN ('borrowed', 'returned', 'overdue')),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

-- 索引优化
CREATE INDEX idx_borrow_records_user ON borrow_records(user_id);
CREATE INDEX idx_borrow_records_book ON borrow_records(book_id);
CREATE INDEX idx_borrow_records_status ON borrow_records(status);

-- 触发器:更新图书可用数量
CREATE OR REPLACE FUNCTION update_book_availability()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.status = 'borrowed' THEN
UPDATE books SET available_copies = available_copies - 1 WHERE id = NEW.book_id;
ELSIF NEW.status = 'returned' AND OLD.status = 'borrowed' THEN
UPDATE books SET available_copies = available_copies + 1 WHERE id = NEW.book_id;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

CREATE TRIGGER trigger_update_book_availability
AFTER INSERT OR UPDATE ON borrow_records
FOR EACH ROW EXECUTE FUNCTION update_book_availability();

5.2 Python实现(基于LangChain)

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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
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
# library_agent.py
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from langchain_openai import ChatOpenAI
from langchain_community.utilities import SQLDatabase
from langchain_community.agent_toolkits import SQLDatabaseToolkit
from langchain_core.tools import tool
import psycopg2
from datetime import datetime, timedelta
import os
from typing import Dict, Any, List

class LibraryAgent:
def __init__(self):
# 初始化数据库连接
self.db = SQLDatabase.from_uri(
f"postgresql://{os.getenv('DB_USER')}:{os.getenv('DB_PASSWORD')}@{os.getenv('DB_HOST')}/{os.getenv('DB_NAME')}"
)

# 初始化LLM
self.llm = ChatOpenAI(
model="gpt-4-turbo",
temperature=0.3,
api_key=os.getenv("OPENAI_API_KEY")
)

# 创建工具包
self.toolkit = SQLDatabaseToolkit(db=self.db, llm=self.llm)

# 定义专业工具
self.tools = [
self.borrow_book_tool,
self.return_book_tool,
self.check_availability_tool,
self.get_user_info_tool,
self.search_books_tool
] + self.toolkit.get_tools()

# 创建Agent
self.agent = self.create_agent()
self.agent_executor = AgentExecutor(
agent=self.agent,
tools=self.tools,
verbose=True,
max_iterations=10,
early_stopping_method="force"
)

def create_agent(self):
"""创建图书馆管理Agent"""
prompt = ChatPromptTemplate.from_messages([
("system", """你是一个专业的图书馆管理助手,负责处理图书借阅、归还、查询等业务。
请根据用户请求选择合适的工具执行操作,并提供清晰、友好的回复。

可用工具:
- borrow_book_tool: 处理图书借阅
- return_book_tool: 处理图书归还
- check_availability_tool: 检查图书可用性
- get_user_info_tool: 获取用户信息
- search_books_tool: 搜索图书

重要规则:
1. 借书前必须检查用户资格和图书可用性
2. 归还图书时需要验证借阅记录
3. 所有操作都需要记录日志
4. 遇到错误时提供友好的错误提示"""),
("human", "{input}"),
("assistant", "{agent_scratchpad}")
])

return create_tool_calling_agent(self.llm, self.tools, prompt)

@tool
def borrow_book_tool(self, user_id: str, book_id: str) -> Dict[str, Any]:
"""借阅图书工具"""
try:
# 检查用户资格
user_info = self.get_user_info_tool(user_id)
if not user_info["eligible"]:
return {"success": False, "message": "用户借阅资格不足"}

# 检查图书可用性
availability = self.check_availability_tool(book_id)
if availability["available_copies"] <= 0:
return {"success": False, "message": "图书暂无库存"}

# 执行借阅
conn = self.get_db_connection()
cursor = conn.cursor()

# 创建借阅记录
borrow_date = datetime.now().date()
due_date = borrow_date + timedelta(days=14) # 14天借阅期

cursor.execute("""
INSERT INTO borrow_records (user_id, book_id, borrow_date, due_date, status)
VALUES (%s, %s, %s, %s, 'borrowed')
RETURNING id, due_date
""", (user_id, book_id, borrow_date, due_date))

result = cursor.fetchone()
transaction_id, due_date = result

conn.commit()
cursor.close()
conn.close()

return {
"success": True,
"transaction_id": transaction_id,
"due_date": due_date.strftime("%Y-%m-%d"),
"message": f"借阅成功!请在{due_date.strftime('%Y-%m-%d')}前归还。"
}

except Exception as e:
return {"success": False, "message": f"借阅失败: {str(e)}"}

@tool
def return_book_tool(self, user_id: str, book_id: str) -> Dict[str, Any]:
"""归还图书工具"""
try:
conn = self.get_db_connection()
cursor = conn.cursor()

# 查找未归还的借阅记录
cursor.execute("""
SELECT id FROM borrow_records
WHERE user_id = %s AND book_id = %s AND status = 'borrowed'
ORDER BY borrow_date DESC LIMIT 1
""", (user_id, book_id))

record = cursor.fetchone()
if not record:
return {"success": False, "message": "未找到相关借阅记录"}

record_id = record[0]

# 更新归还状态
return_date = datetime.now().date()
cursor.execute("""
UPDATE borrow_records
SET status = 'returned', return_date = %s
WHERE id = %s
""", (return_date, record_id))

conn.commit()
cursor.close()
conn.close()

return {
"success": True,
"message": "图书归还成功!感谢您的使用。"
}

except Exception as e:
return {"success": False, "message": f"归还失败: {str(e)}"}

@tool
def check_availability_tool(self, book_id: str) -> Dict[str, Any]:
"""检查图书可用性"""
try:
conn = self.get_db_connection()
cursor = conn.cursor()

cursor.execute("""
SELECT title, author, total_copies, available_copies
FROM books WHERE id = %s
""", (book_id,))

book = cursor.fetchone()
cursor.close()
conn.close()

if not book:
return {"available": False, "message": "图书不存在"}

title, author, total, available = book

return {
"available": available > 0,
"book_title": title,
"author": author,
"total_copies": total,
"available_copies": available,
"message": f"《{title}》当前有{available}本可借"
}

except Exception as e:
return {"error": str(e)}

@tool
def get_user_info_tool(self, user_id: str) -> Dict[str, Any]:
"""获取用户信息"""
try:
conn = self.get_db_connection()
cursor = conn.cursor()

# 获取用户基本信息
cursor.execute("""
SELECT name, email, max_borrow_limit FROM users WHERE id = %s
""", (user_id,))
user = cursor.fetchone()

if not user:
return {"eligible": False, "message": "用户不存在"}

name, email, max_limit = user

# 获取当前借阅数量
cursor.execute("""
SELECT COUNT(*) FROM borrow_records
WHERE user_id = %s AND status = 'borrowed'
""", (user_id,))
current_borrows = cursor.fetchone()[0]

cursor.close()
conn.close()

eligible = current_borrows < max_limit

return {
"eligible": eligible,
"name": name,
"email": email,
"max_borrow_limit": max_limit,
"current_borrows": current_borrows,
"remaining_limit": max_limit - current_borrows,
"message": eligible and "用户借阅资格正常" or "已达到最大借阅数量"
}

except Exception as e:
return {"error": str(e)}

@tool
def search_books_tool(self, query: str, category: str = None) -> List[Dict[str, Any]]:
"""搜索图书"""
try:
conn = self.get_db_connection()
cursor = conn.cursor()

# 构建查询条件
conditions = []
params = [f"%{query}%"]
sql = """
SELECT id, title, author, isbn, category, available_copies
FROM books
WHERE title ILIKE %s OR author ILIKE %s OR isbn ILIKE %s
"""
conditions.extend([f"%{query}%", f"%{query}%", f"%{query}%"])

if category:
sql += " AND category = %s"
conditions.append(category)

sql += " ORDER BY title LIMIT 10"

cursor.execute(sql, tuple(conditions))
books = cursor.fetchall()
cursor.close()
conn.close()

return [
{
"id": book[0],
"title": book[1],
"author": book[2],
"isbn": book[3],
"category": book[4],
"available_copies": book[5]
}
for book in books
]

except Exception as e:
return [{"error": str(e)}]

def get_db_connection(self):
"""获取数据库连接"""
return psycopg2.connect(
host=os.getenv('DB_HOST', 'localhost'),
database=os.getenv('DB_NAME', 'library_db'),
user=os.getenv('DB_USER', 'library_user'),
password=os.getenv('DB_PASSWORD', ''),
port=os.getenv('DB_PORT', '5432')
)

def run(self, user_input: str) -> str:
"""运行Agent处理用户输入"""
result = self.agent_executor.invoke({"input": user_input})
return result["output"]

# 使用示例
if __name__ == "__main__":
# 设置环境变量
os.environ["OPENAI_API_KEY"] = "your-api-key"
os.environ["DB_HOST"] = "localhost"
os.environ["DB_NAME"] = "library_db"
os.environ["DB_USER"] = "library_user"
os.environ["DB_PASSWORD"] = "your-db-password"

# 初始化Agent
agent = LibraryAgent()

# 测试对话
test_inputs = [
"我想借一本《人工智能导论》",
"用户user123可以借多少本书?",
"帮我查找所有编程类的图书",
"我要归还《机器学习实战》"
]

for user_input in test_inputs:
print(f"\n用户: {user_input}")
response = agent.run(user_input)
print(f"助手: {response}")

5.3 Go语言实现

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
package main

import (
"context"
"database/sql"
"fmt"
"log"
"os"
"time"

"github.com/jackc/pgx/v5/pgxpool"
"github.com/sashabaranov/go-openai"
)

type LibraryAgent struct {
db *pgxpool.Pool
openai *openai.Client
config *AgentConfig
skills map[string]Skill
tools map[string]Tool
}

type Skill interface {
Execute(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error)
GetDescription() string
}

type Tool struct {
Name string
Description string
Handler func(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error)
}

type AgentConfig struct {
DatabaseURL string
OpenAIAPIKey string
MaxBorrowDays int
}

func NewLibraryAgent(config *AgentConfig) (*LibraryAgent, error) {
// 初始化数据库连接
dbPool, err := pgxpool.New(context.Background(), config.DatabaseURL)
if err != nil {
return nil, fmt.Errorf("数据库连接失败: %v", err)
}

// 初始化OpenAI客户端
openaiClient := openai.NewClient(config.OpenAIAPIKey)

agent := &LibraryAgent{
db: dbPool,
openai: openaiClient,
config: config,
skills: make(map[string]Skill),
tools: make(map[string]Tool),
}

// 注册技能
agent.registerSkills()

// 注册工具
agent.registerTools()

return agent, nil
}

func (a *LibraryAgent) registerSkills() {
// 借书技能
a.skills["borrow_book"] = &BorrowBookSkill{agent: a}

// 还书技能
a.skills["return_book"] = &ReturnBookSkill{agent: a}

// 查询技能
a.skills["search_books"] = &SearchBooksSkill{agent: a}
}

func (a *LibraryAgent) registerTools() {
a.tools["check_availability"] = Tool{
Name: "check_availability",
Description: "检查图书可用性",
Handler: a.handleCheckAvailability,
}

a.tools["get_user_info"] = Tool{
Name: "get_user_info",
Description: "获取用户信息",
Handler: a.handleGetUserInfo,
}
}

type BorrowBookSkill struct {
agent *LibraryAgent
}

func (s *BorrowBookSkill) Execute(ctx context.Context, params map[string]interface{}) (map[string]interface{}, error) {
userID, ok := params["user_id"].(string)
if !ok {
return nil, fmt.Errorf("缺少用户ID")
}

bookID, ok := params["book_id"].(string)
if !ok {
return nil, fmt.Errorf("缺少图书ID")
}

// 检查用户资格
userInfo, err := s.agent.tools["get_user_info"].Handler(ctx, map[string]interface{}{"user_id": userID})
if err != nil {
return nil, err
}

if !userInfo["eligible"].(bool) {
return map[string]interface{}{
"success": false,
"message": "用户借阅资格不足",
}, nil
}

// 检查图书可用性
availability, err := s.agent.tools["check_availability"].Handler(ctx, map[string]interface{}{"book_id": bookID})
if err != nil {
return nil, err
}

if availability["available_copies"].(int) <= 0 {
return map[string]interface{}{
"success": false,
"message": "图书暂无库存",
}, nil
}

// 执行借阅
tx, err := s.agent.db.Begin(ctx)
if err != nil {
return nil, err
}
defer tx.Rollback(ctx)

borrowDate := time.Now().Format("2006-01-02")
dueDate := time.Now().AddDate(0, 0, s.agent.config.MaxBorrowDays).Format("2006-01-02")

_, err = tx.Exec(ctx, `
INSERT INTO borrow_records (user_id, book_id, borrow_date, due_date, status)
VALUES ($1, $2, $3, $4, 'borrowed')
RETURNING id
`, userID, bookID, borrowDate, dueDate)
if err != nil {
return nil, err
}

if err := tx.Commit(ctx); err != nil {
return nil, err
}

return map[string]interface{}{
"success": true,
"due_date": dueDate,
"message": fmt.Sprintf("借阅成功!请在%s前归还。", dueDate),
}, nil
}

func (s *BorrowBookSkill) GetDescription() string {
return "处理图书借阅请求,验证用户资格和图书可用性"
}

// 使用示例
func main() {
config := &AgentConfig{
DatabaseURL: "postgres://library_user:password@localhost:5432/library_db",
OpenAIAPIKey: os.Getenv("OPENAI_API_KEY"),
MaxBorrowDays: 14,
}

agent, err := NewLibraryAgent(config)
if err != nil {
log.Fatalf("初始化Agent失败: %v", err)
}

defer agent.db.Close()

// 测试借书技能
ctx := context.Background()
params := map[string]interface{}{
"user_id": "user123",
"book_id": "book456",
}

result, err := agent.skills["borrow_book"].Execute(ctx, params)
if err != nil {
log.Printf("技能执行失败: %v", err)
return
}

fmt.Printf("结果: %+v\n", result)
}

5.4 PHP实现

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
209
210
211
212
213
214
215
216
217
<?php
// library_agent.php

require_once 'vendor/autoload.php';

use OpenAI\Client;
use OpenAI\Factory;
use PgSql\Connection;

class LibraryAgent {
private $db;
private $openai;
private $skills = [];
private $tools = [];
private $config;

public function __construct($config) {
$this->config = $config;

// 初始化数据库连接
$this->db = pg_connect($config['database_url']);
if (!$this->db) {
throw new Exception("数据库连接失败: " . pg_last_error());
}

// 初始化OpenAI客户端
$this->openai = Factory::factory()->getClient();

// 注册技能
$this->registerSkills();

// 注册工具
$this->registerTools();
}

private function registerSkills() {
$this->skills['borrow_book'] = new BorrowBookSkill($this);
$this->skills['return_book'] = new ReturnBookSkill($this);
$this->skills['search_books'] = new SearchBooksSkill($this);
}

private function registerTools() {
$this->tools['check_availability'] = [
'name' => 'check_availability',
'description' => '检查图书可用性',
'handler' => [$this, 'handleCheckAvailability']
];

$this->tools['get_user_info'] = [
'name' => 'get_user_info',
'description' => '获取用户信息',
'handler' => [$this, 'handleGetUserInfo']
];
}

public function executeSkill($skillName, $params) {
if (!isset($this->skills[$skillName])) {
throw new Exception("技能不存在: " . $skillName);
}

return $this->skills[$skillName]->execute($params);
}

public function handleCheckAvailability($params) {
$bookId = $params['book_id'] ?? '';

$query = "SELECT title, author, total_copies, available_copies FROM books WHERE id = $1";
$result = pg_query_params($this->db, $query, [$bookId]);

if (!$result || pg_num_rows($result) === 0) {
return [
'available' => false,
'message' => '图书不存在'
];
}

$book = pg_fetch_assoc($result);
$available = (int)$book['available_copies'] > 0;

return [
'available' => $available,
'book_title' => $book['title'],
'author' => $book['author'],
'total_copies' => (int)$book['total_copies'],
'available_copies' => (int)$book['available_copies'],
'message' => $available ? "《{$book['title']}》当前有{$book['available_copies']}本可借" : "《{$book['title']}》暂无库存"
];
}

public function handleGetUserInfo($params) {
$userId = $params['user_id'] ?? '';

$query = "SELECT name, email, max_borrow_limit FROM users WHERE id = $1";
$result = pg_query_params($this->db, $query, [$userId]);

if (!$result || pg_num_rows($result) === 0) {
return [
'eligible' => false,
'message' => '用户不存在'
];
}

$user = pg_fetch_assoc($result);

// 获取当前借阅数量
$borrowQuery = "SELECT COUNT(*) as current_borrows FROM borrow_records WHERE user_id = $1 AND status = 'borrowed'";
$borrowResult = pg_query_params($this->db, $borrowQuery, [$userId]);
$borrowData = pg_fetch_assoc($borrowResult);
$currentBorrows = (int)$borrowData['current_borrows'];
$maxLimit = (int)$user['max_borrow_limit'];
$eligible = $currentBorrows < $maxLimit;

return [
'eligible' => $eligible,
'name' => $user['name'],
'email' => $user['email'],
'max_borrow_limit' => $maxLimit,
'current_borrows' => $currentBorrows,
'remaining_limit' => $maxLimit - $currentBorrows,
'message' => $eligible ? "用户借阅资格正常" : "已达到最大借阅数量"
];
}

public function __destruct() {
if ($this->db) {
pg_close($this->db);
}
}
}

class BorrowBookSkill {
private $agent;

public function __construct($agent) {
$this->agent = $agent;
}

public function execute($params) {
$userId = $params['user_id'] ?? '';
$bookId = $params['book_id'] ?? '';

if (empty($userId) || empty($bookId)) {
throw new Exception("缺少必要的参数: user_id 或 book_id");
}

// 检查用户资格
$userInfo = $this->agent->tools['get_user_info']['handler']([
'user_id' => $userId
]);

if (!$userInfo['eligible']) {
return [
'success' => false,
'message' => $userInfo['message']
];
}

// 检查图书可用性
$availability = $this->agent->tools['check_availability']['handler']([
'book_id' => $bookId
]);

if (!$availability['available']) {
return [
'success' => false,
'message' => $availability['message']
];
}

// 执行借阅
$borrowDate = date('Y-m-d');
$dueDate = date('Y-m-d', strtotime("+14 days"));

$query = "INSERT INTO borrow_records (user_id, book_id, borrow_date, due_date, status) VALUES ($1, $2, $3, $4, 'borrowed') RETURNING id";
$result = pg_query_params($this->agent->db, $query, [$userId, $bookId, $borrowDate, $dueDate]);

if (!$result) {
throw new Exception("借阅失败: " . pg_last_error($this->agent->db));
}

$record = pg_fetch_assoc($result);
$transactionId = $record['id'];

return [
'success' => true,
'transaction_id' => $transactionId,
'due_date' => $dueDate,
'message' => "借阅成功!请在{$dueDate}前归还。"
];
}

public function getDescription() {
return "处理图书借阅请求,验证用户资格和图书可用性";
}
}

// 使用示例
$config = [
'database_url' => 'host=localhost dbname=library_db user=library_user password=password',
'openai_api_key' => getenv('OPENAI_API_KEY')
];

try {
$agent = new LibraryAgent($config);

// 执行借书技能
$result = $agent->executeSkill('borrow_book', [
'user_id' => 'user123',
'book_id' => 'book456'
]);

echo json_encode($result, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE);

} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
?>

六、性能优化与监控

6.1 性能优化策略

  1. 技能缓存:对频繁调用的Skills结果进行缓存,减少重复计算
  2. 连接池优化:合理配置数据库连接池大小,避免连接泄漏
  3. 异步执行:对于耗时操作,使用异步任务队列处理
  4. 批处理:合并多个相似请求,减少数据库查询次数
  5. 索引优化:为常用查询字段创建合适的索引

6.2 监控指标设计

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
# prometheus.yml
scrape_configs:
- job_name: 'library-agent'
static_configs:
- targets: ['agent-service:9090']
metrics_path: '/metrics'
scrape_interval: 15s

# 关键监控指标
metrics:
- name: "agent_skill_execution_count"
type: "counter"
description: "技能执行次数统计"
labels: ["skill_name", "status"]

- name: "agent_skill_execution_duration_seconds"
type: "histogram"
description: "技能执行耗时分布"
labels: ["skill_name"]

- name: "agent_database_connections"
type: "gauge"
description: "数据库连接数"

- name: "agent_memory_usage_mb"
type: "gauge"
description: "内存使用量"

- name: "agent_request_queue_length"
type: "gauge"
description: "请求队列长度"

- name: "agent_error_count"
type: "counter"
description: "错误次数统计"
labels: ["error_type", "skill_name"]

七、安全加固措施

7.1 安全架构设计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
┌─────────────────────────────────────────────────────────────┐
│ 安全防护层 |
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ 输入验证 │ | 权限控制 │ │ 沙箱隔离 │ │
│ │ (Validation)│ │ (RBAC) │ │ (Sandbox) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└───────────────────────────┬─────────────────────────────────┘

┌───────────────────────────▼─────────────────────────────────┐
│ 应用层 │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Agent | │ Skills │ │ Log │ │
| | 核心 | | 管理 | | 日志审计 | |
│ │ (Core) │ │ (Registry) │ │ (Audit) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘

7.2 关键安全措施(建议)

  1. 输入验证:对所有用户输入进行严格验证和过滤
  2. 权限控制:基于角色的访问控制(RBAC),最小权限原则
  3. 沙箱隔离:Skills在独立的沙箱环境中执行,限制系统访问
  4. 数据加密:敏感数据在传输和存储时进行加密
  5. 审计日志:记录所有关键操作,便于安全审计和问题追踪
  6. 速率限制:防止API滥用和DDoS攻击
  7. 安全更新:定期更新依赖库,修复已知安全漏洞

结语

Agent与Skills的关系架构代表了现代AI系统设计的新范式,通过将复杂的业务逻辑分解为可复用的Skills,Agent能够更加灵活、智能地处理各种任务。本文基于Debian 12环境,从原理到实践,全面介绍了这一架构的设计、实现和部署。LangChain框架为开发者提供了强大的工具和预构建的架构,让构建AI agents变得更加高效。

在图书馆借书管理系统的实战Demo中,我们看到了如何将理论知识应用到具体业务场景,通过Python、Go、PHP三种主流语言的实现,展示了Skills架构的跨语言特性。 无论选择哪种技术栈,核心的设计原则都是相同的:模块化、可复用、安全可靠。

随着AI技术的不断发展,Agent-Skills架构将继续演进,支持更复杂的多Agent协同、更智能的技能发现和组合机制。作为开发者,我们需要持续关注这一领域的发展,掌握最新的设计模式和最佳实践,构建更加智能、可靠的应用系统。

个人建议:技术的核心是解决问题,而不是追逐潮流。在设计Agent-Skills架构时,始终要从业务需求出发,选择最适合的技术方案,而不是最流行的技术方案。只有这样,我们才能构建真正有价值的AI系统。skill本身也许是一种解决方案,但绝不是唯一的最佳解决方案,随着模型的能力进一步发展,针对企业级场景应该会有更好的解决方案。

Agent 与 Skills 之间的区别通过一个简单图书馆借阅系统案例实践指南

https://www.wdft.com/65d4601b.html

Author

Jaco Liu

Posted on

2026-01-24

Updated on

2026-01-24

Licensed under