asktable-advisor 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,195 @@
1
+ """LLM client for AskTable Advisor using Aliyun Bailian (qwen3-max)."""
2
+
3
+ import logging
4
+ from typing import Any, Dict, List, Optional, Union
5
+
6
+ from anthropic import Anthropic
7
+ from anthropic.types import (
8
+ Message,
9
+ MessageParam,
10
+ ToolUseBlock,
11
+ TextBlock,
12
+ ContentBlock,
13
+ )
14
+
15
+ from ..config import AdvisorSettings
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ class LLMClient:
21
+ """
22
+ LLM client for the Advisor Agent.
23
+
24
+ Uses Anthropic SDK with Aliyun Bailian backend (qwen3-max model).
25
+ Supports Tool Use (function calling) for agent operations.
26
+ """
27
+
28
+ def __init__(self, settings: AdvisorSettings):
29
+ """
30
+ Initialize LLM client.
31
+
32
+ Args:
33
+ settings: Application settings with API credentials
34
+ """
35
+ self.settings = settings
36
+
37
+ # Initialize Anthropic client with Aliyun Bailian endpoint
38
+ self.client = Anthropic(
39
+ api_key=settings.anthropic_api_key,
40
+ base_url=settings.anthropic_base_url,
41
+ )
42
+
43
+ self.model = settings.agent_model
44
+ self.temperature = settings.agent_temperature
45
+ self.max_tokens = settings.agent_max_tokens
46
+
47
+ logger.info(
48
+ f"LLM client initialized: model={self.model}, "
49
+ f"base_url={settings.anthropic_base_url}"
50
+ )
51
+
52
+ def create_message(
53
+ self,
54
+ messages: List[MessageParam],
55
+ system: Optional[str] = None,
56
+ tools: Optional[List[Dict[str, Any]]] = None,
57
+ stream: bool = False,
58
+ ) -> Message:
59
+ """
60
+ Create a message with the LLM.
61
+
62
+ Args:
63
+ messages: Conversation history (user/assistant messages)
64
+ system: System prompt (optional)
65
+ tools: Available tools for function calling (optional)
66
+ stream: Whether to stream the response (default: False)
67
+
68
+ Returns:
69
+ Message object with LLM response
70
+ """
71
+ try:
72
+ params = {
73
+ "model": self.model,
74
+ "messages": messages,
75
+ "max_tokens": self.max_tokens,
76
+ "temperature": self.temperature,
77
+ }
78
+
79
+ if system:
80
+ params["system"] = system
81
+
82
+ if tools:
83
+ params["tools"] = tools
84
+
85
+ logger.debug(f"Creating message with {len(messages)} messages")
86
+
87
+ if stream:
88
+ # Streaming not implemented yet
89
+ # TODO: Implement streaming response handling
90
+ logger.warning("Streaming not yet implemented, falling back to non-streaming")
91
+
92
+ response = self.client.messages.create(**params)
93
+
94
+ logger.debug(
95
+ f"Received response: "
96
+ f"stop_reason={response.stop_reason}, "
97
+ f"usage={response.usage}"
98
+ )
99
+
100
+ return response
101
+
102
+ except Exception as e:
103
+ logger.error(f"Error creating message: {e}")
104
+ raise
105
+
106
+ def extract_text(self, message: Message) -> str:
107
+ """
108
+ Extract text content from a message.
109
+
110
+ Args:
111
+ message: Message object from LLM
112
+
113
+ Returns:
114
+ Concatenated text from all text blocks
115
+ """
116
+ text_parts = []
117
+ for block in message.content:
118
+ if isinstance(block, TextBlock):
119
+ text_parts.append(block.text)
120
+ return "\n".join(text_parts)
121
+
122
+ def extract_tool_uses(self, message: Message) -> List[ToolUseBlock]:
123
+ """
124
+ Extract tool use blocks from a message.
125
+
126
+ Args:
127
+ message: Message object from LLM
128
+
129
+ Returns:
130
+ List of tool use blocks
131
+ """
132
+ tool_uses = []
133
+ for block in message.content:
134
+ if isinstance(block, ToolUseBlock):
135
+ tool_uses.append(block)
136
+ return tool_uses
137
+
138
+ def has_tool_use(self, message: Message) -> bool:
139
+ """
140
+ Check if message contains tool use.
141
+
142
+ Args:
143
+ message: Message object from LLM
144
+
145
+ Returns:
146
+ True if message contains at least one tool use block
147
+ """
148
+ return any(isinstance(block, ToolUseBlock) for block in message.content)
149
+
150
+ def format_tool_result(
151
+ self,
152
+ tool_use_id: str,
153
+ content: Union[str, Dict[str, Any]],
154
+ is_error: bool = False,
155
+ ) -> Dict[str, Any]:
156
+ """
157
+ Format tool execution result for sending back to LLM.
158
+
159
+ Args:
160
+ tool_use_id: ID of the tool use block
161
+ content: Result content (string or dict)
162
+ is_error: Whether this is an error result
163
+
164
+ Returns:
165
+ Formatted tool result message
166
+ """
167
+ if isinstance(content, dict):
168
+ import json
169
+ content_str = json.dumps(content, ensure_ascii=False, indent=2)
170
+ else:
171
+ content_str = str(content)
172
+
173
+ return {
174
+ "type": "tool_result",
175
+ "tool_use_id": tool_use_id,
176
+ "content": content_str,
177
+ "is_error": is_error,
178
+ }
179
+
180
+ def test_connection(self) -> bool:
181
+ """
182
+ Test LLM API connection.
183
+
184
+ Returns:
185
+ True if connection successful, False otherwise
186
+ """
187
+ try:
188
+ response = self.create_message(
189
+ messages=[{"role": "user", "content": "Hello"}],
190
+ system="You are a test assistant. Reply with 'OK'.",
191
+ )
192
+ return response.stop_reason in ["end_turn", "stop_sequence"]
193
+ except Exception as e:
194
+ logger.error(f"Connection test failed: {e}")
195
+ return False
@@ -0,0 +1,135 @@
1
+ """System prompts for AskTable Advisor Agent."""
2
+
3
+ # Main system prompt for the Advisor Agent
4
+ SYSTEM_PROMPT = """你是 AskTable Advisor,一个专业的 AskTable 场景构建助手 AI Agent。
5
+
6
+ ## 你的职责
7
+
8
+ 1. **创建演示场景**:根据用户描述的业务场景(如"电商平台"、"在线教育"、"CRM 系统"等),自动设计和构建完整的 AskTable 演示项目
9
+ 2. **管理现有场景**:检查、完善和优化用户 AskTable 项目中已有的场景
10
+ 3. **提供专业建议**:基于 AskTable 最佳实践,给出专业的数据建模和配置建议
11
+
12
+ ## 你的能力
13
+
14
+ ### 数据建模专家
15
+ - 精通各种业务场景的数据库设计(电商、CRM、ERP、教育、医疗、金融等)
16
+ - 能够分析业务需求,设计合理的表结构、字段、关系
17
+ - 了解数据库规范化原则和性能优化
18
+
19
+ ### AskTable 专家
20
+ - 深入理解 AskTable 的核心概念:Datasource、Bot、Chat、Role、Policy
21
+ - 熟悉 AskTable 的最佳实践和配置技巧
22
+ - 能够优化 Bot 配置,提升用户体验
23
+
24
+ ### 数据生成专家
25
+ - 能够生成真实感、符合业务逻辑的虚拟数据
26
+ - 确保数据之间的关联关系正确(外键约束、业务规则等)
27
+ - 生成的数据具有多样性和代表性
28
+
29
+ ## 工作方式
30
+
31
+ ### 1. 对话式交互
32
+ - 通过**多轮对话**理解用户需求
33
+ - 主动询问关键细节(业务范围、数据规模、特殊需求等)
34
+ - 在执行重要操作前向用户确认
35
+
36
+ ### 2. 渐进式构建
37
+ 完整的场景构建流程:
38
+ 1. **需求分析**:理解场景、询问细节
39
+ 2. **数据库设计**:设计表结构(调用 design_database_schema)
40
+ 3. **数据生成**:生成虚拟数据(调用 generate_sample_data)
41
+ 4. **数据库创建**:执行 SQL(调用 execute_sql)
42
+ 5. **创建 Datasource**:连接数据库(调用 create_asktable_datasource)
43
+ 6. **创建 Bot**:配置智能助手(调用 create_asktable_bot)
44
+ 7. **创建 Chat**:预制演示对话(调用 create_asktable_chat)
45
+ 8. **配置权限**(可选):创建角色和权限策略(调用 create_permissions)
46
+
47
+ ### 3. 清晰的进度反馈
48
+ - 在每个步骤开始前说明将要做什么
49
+ - 使用进度标识:[1/6]、[2/6] 等
50
+ - 展示关键结果(如生成了多少数据、创建了哪些资源)
51
+ - 操作完成后提供访问链接或 ID
52
+
53
+ ## AskTable 最佳实践
54
+
55
+ ### Datasource 配置
56
+ - 使用清晰的命名:"{场景名}演示数据库"
57
+ - 添加描述性说明,帮助用户理解数据内容
58
+ - 确保数据库连接信息正确
59
+
60
+ ### Bot 配置
61
+ 1. **命名**:使用角色化命名,如"电商数据分析师"、"CRM 助手"
62
+ 2. **系统指令(Instructions)**:
63
+ - 明确 Bot 的角色定位
64
+ - 说明 Bot 能回答哪些类型的问题
65
+ - 提供分析方法和建议格式
66
+ - 强调数据驱动的回答方式
67
+ 3. **示例问题(Sample Questions)**:
68
+ - 提供 15-25 个精心设计的示例问题
69
+ - 覆盖不同的分析维度(趋势、对比、排名、异常等)
70
+ - 从简单到复杂,层次分明
71
+ - 符合实际业务场景
72
+
73
+ ### Chat 配置
74
+ - 预制 3-5 个典型的分析场景
75
+ - 展示 Bot 的核心能力
76
+ - 问题之间有逻辑递进关系
77
+
78
+ ### 权限配置
79
+ - 根据实际业务角色设计 Role(如管理员、经理、分析师、员工)
80
+ - Policy 要合理:行级权限(row-level)和列级权限(column-level)
81
+ - 确保权限规则清晰、易于理解
82
+
83
+ ## 数据库设计原则
84
+
85
+ ### 表结构设计
86
+ 1. **主键**:每个表都要有主键(通常是自增 ID)
87
+ 2. **外键**:正确设置外键关系,确保数据一致性
88
+ 3. **时间戳**:添加 created_at、updated_at 字段
89
+ 4. **索引**:在查询频繁的字段上添加索引
90
+ 5. **字段类型**:选择合适的数据类型(INT、VARCHAR、DECIMAL、DATETIME 等)
91
+
92
+ ### 数据生成原则
93
+ 1. **真实感**:姓名、地址、商品名等要符合真实习惯
94
+ 2. **关联正确**:外键关系要正确(如订单的 user_id 必须存在于 users 表)
95
+ 3. **分布合理**:数据分布要有一定规律(如销售额符合正态分布、时间分布均匀等)
96
+ 4. **业务逻辑**:符合业务规则(如订单金额 = 商品价格 × 数量,订单状态变化合理等)
97
+
98
+ ## 处理用户请求的策略
99
+
100
+ ### 创建新场景
101
+ 1. 识别场景类型(电商、教育、CRM 等)
102
+ 2. 询问关键信息:
103
+ - 业务范围(需要哪些功能模块)
104
+ - 数据规模(用户数、订单数等)
105
+ - 特殊需求(是否需要权限、多租户等)
106
+ 3. 调用工具完成构建
107
+ 4. 给出清晰的完成报告
108
+
109
+ ### 查看现有场景
110
+ 1. 调用 inspect_asktable_project
111
+ 2. 整理并展示资源列表
112
+ 3. 识别可优化的地方
113
+ 4. 主动提供改进建议
114
+
115
+ ### 完善/优化场景
116
+ 1. 理解用户的优化意图
117
+ 2. 分析现有配置
118
+ 3. 提出优化方案并征求确认
119
+ 4. 执行优化操作
120
+
121
+ ## 重要提示
122
+
123
+ 1. **主动性**:不要被动等待,主动询问必要的信息
124
+ 2. **确认性**:在执行重要操作(如删除、大量修改)前,一定要确认
125
+ 3. **专业性**:给出的建议要基于最佳实践,有理有据
126
+ 4. **友好性**:语言要专业但易懂,避免过多技术术语
127
+ 5. **完整性**:创建场景要完整(数据库 + Datasource + Bot + Chat),不要遗漏步骤
128
+
129
+ 现在,请根据用户的请求,使用你的工具和专业知识来帮助他们!
130
+ """
131
+
132
+
133
+ def get_system_prompt() -> str:
134
+ """Get the main system prompt for the Advisor Agent."""
135
+ return SYSTEM_PROMPT
@@ -0,0 +1,324 @@
1
+ """Tool definitions for AskTable Advisor Agent."""
2
+
3
+ from typing import Any, Dict, List
4
+
5
+ # Tool definitions following Anthropic's Tool Use specification
6
+ # https://docs.anthropic.com/claude/docs/tool-use
7
+
8
+
9
+ def get_tools() -> List[Dict[str, Any]]:
10
+ """
11
+ Get all available tools for the Advisor Agent.
12
+
13
+ Returns:
14
+ List of tool definitions
15
+ """
16
+ return [
17
+ {
18
+ "name": "inspect_asktable_project",
19
+ "description": (
20
+ "检查用户的 AskTable 项目,获取所有现有资源。"
21
+ "返回项目中的 datasources、bots、chats、roles、policies 等资源列表。"
22
+ "用于了解项目当前状态,查看已有场景。"
23
+ ),
24
+ "input_schema": {
25
+ "type": "object",
26
+ "properties": {
27
+ "detailed": {
28
+ "type": "boolean",
29
+ "description": "是否返回详细信息(包括每个资源的完整配置)",
30
+ "default": False,
31
+ }
32
+ },
33
+ "required": [],
34
+ },
35
+ },
36
+ {
37
+ "name": "design_database_schema",
38
+ "description": (
39
+ "根据场景描述,自动设计数据库表结构。"
40
+ "分析业务需求,确定需要的表、字段、数据类型、主键、外键关系等。"
41
+ "返回 SQL DDL 语句(CREATE TABLE 语句)。"
42
+ ),
43
+ "input_schema": {
44
+ "type": "object",
45
+ "properties": {
46
+ "scenario_name": {
47
+ "type": "string",
48
+ "description": "场景名称,如'电商平台'、'在线教育'、'CRM 系统'",
49
+ },
50
+ "scenario_description": {
51
+ "type": "string",
52
+ "description": "场景的详细描述,包括核心业务、主要功能等",
53
+ },
54
+ "requirements": {
55
+ "type": "array",
56
+ "items": {"type": "string"},
57
+ "description": "具体的业务需求列表,如'需要订单管理'、'需要用户等级'",
58
+ },
59
+ "scale_info": {
60
+ "type": "object",
61
+ "description": "数据规模信息",
62
+ "properties": {
63
+ "user_count": {"type": "integer", "description": "预期用户数"},
64
+ "order_count": {"type": "integer", "description": "预期订单数"},
65
+ "other": {"type": "string", "description": "其他规模信息"},
66
+ },
67
+ },
68
+ },
69
+ "required": ["scenario_name", "scenario_description"],
70
+ },
71
+ },
72
+ {
73
+ "name": "generate_sample_data",
74
+ "description": (
75
+ "为给定的数据库表结构生成真实感的虚拟数据。"
76
+ "使用 LLM 生成符合业务逻辑、数据关联正确的示例数据。"
77
+ "返回 SQL INSERT 语句。"
78
+ ),
79
+ "input_schema": {
80
+ "type": "object",
81
+ "properties": {
82
+ "schema_sql": {
83
+ "type": "string",
84
+ "description": "数据库表结构的 SQL DDL 语句",
85
+ },
86
+ "scenario_context": {
87
+ "type": "string",
88
+ "description": "场景上下文,帮助生成更真实的数据",
89
+ },
90
+ "data_volume": {
91
+ "type": "object",
92
+ "description": "每个表需要生成的数据量",
93
+ "additionalProperties": {"type": "integer"},
94
+ },
95
+ },
96
+ "required": ["schema_sql", "data_volume"],
97
+ },
98
+ },
99
+ {
100
+ "name": "execute_sql",
101
+ "description": (
102
+ "在数据库中执行 SQL 语句。"
103
+ "可用于创建表结构(DDL)或插入数据(DML)。"
104
+ "执行前会显示将要执行的 SQL 预览。"
105
+ ),
106
+ "input_schema": {
107
+ "type": "object",
108
+ "properties": {
109
+ "sql": {
110
+ "type": "string",
111
+ "description": "要执行的 SQL 语句",
112
+ },
113
+ "description": {
114
+ "type": "string",
115
+ "description": "对这个 SQL 操作的描述,如'创建用户表'、'插入订单数据'",
116
+ },
117
+ },
118
+ "required": ["sql", "description"],
119
+ },
120
+ },
121
+ {
122
+ "name": "create_asktable_datasource",
123
+ "description": (
124
+ "在 AskTable 中创建数据源(Datasource)。"
125
+ "连接到指定的数据库,使其可以被 Bot 查询和分析。"
126
+ ),
127
+ "input_schema": {
128
+ "type": "object",
129
+ "properties": {
130
+ "name": {
131
+ "type": "string",
132
+ "description": "数据源名称,如'电商演示数据库'",
133
+ },
134
+ "description": {
135
+ "type": "string",
136
+ "description": "数据源描述",
137
+ },
138
+ "database_name": {
139
+ "type": "string",
140
+ "description": "要连接的数据库名称",
141
+ },
142
+ },
143
+ "required": ["name", "database_name"],
144
+ },
145
+ },
146
+ {
147
+ "name": "create_asktable_bot",
148
+ "description": (
149
+ "在 AskTable 中创建智能 Bot。"
150
+ "配置 Bot 的业务知识、系统指令、示例问题等。"
151
+ "Bot 可以回答用户关于数据的问题,执行数据分析。"
152
+ ),
153
+ "input_schema": {
154
+ "type": "object",
155
+ "properties": {
156
+ "name": {
157
+ "type": "string",
158
+ "description": "Bot 名称,如'电商数据分析师'、'教育平台分析助手'",
159
+ },
160
+ "description": {
161
+ "type": "string",
162
+ "description": "Bot 描述",
163
+ },
164
+ "datasource_id": {
165
+ "type": "string",
166
+ "description": "关联的数据源 ID",
167
+ },
168
+ "instructions": {
169
+ "type": "string",
170
+ "description": "Bot 的系统指令,定义其角色、能力、分析方式",
171
+ },
172
+ "sample_questions": {
173
+ "type": "array",
174
+ "items": {"type": "string"},
175
+ "description": "示例问题列表,展示 Bot 能回答的典型问题",
176
+ },
177
+ },
178
+ "required": ["name", "datasource_id", "instructions"],
179
+ },
180
+ },
181
+ {
182
+ "name": "enhance_bot",
183
+ "description": (
184
+ "增强现有的 Bot。"
185
+ "可以添加更多示例问题、优化系统指令、更新业务知识等。"
186
+ ),
187
+ "input_schema": {
188
+ "type": "object",
189
+ "properties": {
190
+ "bot_id": {
191
+ "type": "string",
192
+ "description": "要增强的 Bot ID",
193
+ },
194
+ "action": {
195
+ "type": "string",
196
+ "enum": ["add_questions", "update_instructions", "optimize"],
197
+ "description": "增强动作类型",
198
+ },
199
+ "content": {
200
+ "type": "object",
201
+ "description": "根据 action 类型提供的内容",
202
+ "properties": {
203
+ "questions": {
204
+ "type": "array",
205
+ "items": {"type": "string"},
206
+ "description": "要添加的示例问题(action=add_questions 时)",
207
+ },
208
+ "instructions": {
209
+ "type": "string",
210
+ "description": "更新的系统指令(action=update_instructions 时)",
211
+ },
212
+ },
213
+ },
214
+ },
215
+ "required": ["bot_id", "action"],
216
+ },
217
+ },
218
+ {
219
+ "name": "create_asktable_chat",
220
+ "description": (
221
+ "在 AskTable 中创建演示对话(Chat)。"
222
+ "预制一些典型的业务问题和分析场景,方便演示和测试。"
223
+ ),
224
+ "input_schema": {
225
+ "type": "object",
226
+ "properties": {
227
+ "name": {
228
+ "type": "string",
229
+ "description": "Chat 名称,如'电商场景演示'",
230
+ },
231
+ "bot_id": {
232
+ "type": "string",
233
+ "description": "关联的 Bot ID",
234
+ },
235
+ "initial_messages": {
236
+ "type": "array",
237
+ "items": {"type": "string"},
238
+ "description": "预制的问题列表",
239
+ },
240
+ },
241
+ "required": ["name", "bot_id"],
242
+ },
243
+ },
244
+ {
245
+ "name": "create_permissions",
246
+ "description": (
247
+ "创建权限配置(Roles 和 Policies)。"
248
+ "定义不同角色(如管理员、分析师、普通用户)的数据访问权限。"
249
+ "配置行级、列级的数据访问控制。"
250
+ ),
251
+ "input_schema": {
252
+ "type": "object",
253
+ "properties": {
254
+ "datasource_id": {
255
+ "type": "string",
256
+ "description": "关联的数据源 ID",
257
+ },
258
+ "roles": {
259
+ "type": "array",
260
+ "items": {
261
+ "type": "object",
262
+ "properties": {
263
+ "name": {"type": "string"},
264
+ "description": {"type": "string"},
265
+ },
266
+ },
267
+ "description": "角色定义列表",
268
+ },
269
+ "policies": {
270
+ "type": "array",
271
+ "items": {
272
+ "type": "object",
273
+ "properties": {
274
+ "role_name": {"type": "string"},
275
+ "table": {"type": "string"},
276
+ "rules": {"type": "object"},
277
+ },
278
+ },
279
+ "description": "权限策略列表",
280
+ },
281
+ },
282
+ "required": ["datasource_id", "roles"],
283
+ },
284
+ },
285
+ {
286
+ "name": "ask_user",
287
+ "description": (
288
+ "向用户询问问题以获取更多信息。"
289
+ "当需要用户确认、选择或提供额外细节时使用此工具。"
290
+ ),
291
+ "input_schema": {
292
+ "type": "object",
293
+ "properties": {
294
+ "question": {
295
+ "type": "string",
296
+ "description": "要问用户的问题",
297
+ },
298
+ "options": {
299
+ "type": "array",
300
+ "items": {"type": "string"},
301
+ "description": "可选的选项列表(如果是多选题)",
302
+ },
303
+ },
304
+ "required": ["question"],
305
+ },
306
+ },
307
+ ]
308
+
309
+
310
+ def get_tool_by_name(name: str) -> Dict[str, Any]:
311
+ """
312
+ Get a specific tool definition by name.
313
+
314
+ Args:
315
+ name: Tool name
316
+
317
+ Returns:
318
+ Tool definition dict, or None if not found
319
+ """
320
+ tools = get_tools()
321
+ for tool in tools:
322
+ if tool["name"] == name:
323
+ return tool
324
+ return None
File without changes