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,18 @@
1
+ """
2
+ AskTable Advisor - AI Agent for AskTable scenario building.
3
+
4
+ A conversational AI agent that helps users quickly create and manage
5
+ high-quality AskTable demo projects.
6
+ """
7
+
8
+ from .__version__ import __version__, __version_info__
9
+ from .config import AdvisorSettings, get_settings
10
+ from .agent.advisor import AdvisorAgent
11
+
12
+ __all__ = [
13
+ "__version__",
14
+ "__version_info__",
15
+ "AdvisorSettings",
16
+ "get_settings",
17
+ "AdvisorAgent",
18
+ ]
@@ -0,0 +1,156 @@
1
+ """Command-line interface for AskTable Advisor."""
2
+
3
+ import sys
4
+ import logging
5
+ from pathlib import Path
6
+
7
+ from rich.console import Console
8
+ from rich.panel import Panel
9
+ from rich.markdown import Markdown
10
+ from rich.prompt import Prompt
11
+
12
+ from .config import AdvisorSettings, get_settings
13
+ from .agent.advisor import AdvisorAgent
14
+
15
+ # Setup console
16
+ console = Console()
17
+
18
+ # Setup logging
19
+ logging.basicConfig(
20
+ level=logging.INFO,
21
+ format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
22
+ handlers=[
23
+ logging.FileHandler("asktable_advisor.log"),
24
+ logging.StreamHandler(sys.stderr),
25
+ ],
26
+ )
27
+
28
+
29
+ def print_welcome() -> None:
30
+ """Print welcome message."""
31
+ welcome_text = """
32
+ # 🤖 AskTable Advisor v1.0
33
+
34
+ 你好!我是 AskTable Advisor,你的 AskTable 场景构建助手。
35
+
36
+ ## 我能帮你:
37
+ - 🎨 **创建新场景**:告诉我场景名称(如"电商平台"、"在线教育"),我会自动设计并构建完整的演示项目
38
+ - 📊 **查看现有场景**:检查你的 AskTable 项目中已有的资源
39
+ - ✨ **完善场景**:优化现有场景,添加更多功能
40
+
41
+ ## 使用方式:
42
+ 直接用自然语言告诉我你想做什么!例如:
43
+ - "创建一个在线教育平台的演示场景"
44
+ - "看看我现在有哪些场景"
45
+ - "给电商 Bot 添加更多示例问题"
46
+
47
+ 输入 **exit** 或 **quit** 退出。
48
+
49
+ 开始对话吧!
50
+ """
51
+ console.print(Panel(Markdown(welcome_text), border_style="cyan"))
52
+
53
+
54
+ def run_chat_loop(agent: AdvisorAgent) -> None:
55
+ """
56
+ Run the main chat loop.
57
+
58
+ Args:
59
+ agent: Advisor agent instance
60
+ """
61
+ while True:
62
+ try:
63
+ # Get user input
64
+ user_input = Prompt.ask("\n[bold cyan]你[/bold cyan]").strip()
65
+
66
+ # Check for exit commands
67
+ if user_input.lower() in ["exit", "quit", "退出", "再见"]:
68
+ console.print("\n[dim]再见!如有需要随时回来 👋[/dim]\n")
69
+ break
70
+
71
+ # Skip empty input
72
+ if not user_input:
73
+ continue
74
+
75
+ # Process with agent
76
+ console.print("\n[bold green]Advisor[/bold green]: ", end="")
77
+
78
+ try:
79
+ response = agent.chat(user_input)
80
+ console.print(response)
81
+
82
+ except Exception as e:
83
+ console.print(f"\n[red]错误:{str(e)}[/red]")
84
+ logging.error(f"Error processing message: {e}", exc_info=True)
85
+
86
+ except KeyboardInterrupt:
87
+ console.print("\n\n[dim]检测到 Ctrl+C,退出程序...[/dim]\n")
88
+ break
89
+
90
+ except EOFError:
91
+ console.print("\n\n[dim]输入结束,退出程序...[/dim]\n")
92
+ break
93
+
94
+
95
+ def test_connections(agent: AdvisorAgent) -> bool:
96
+ """
97
+ Test all connections.
98
+
99
+ Args:
100
+ agent: Advisor agent instance
101
+
102
+ Returns:
103
+ True if all connections successful
104
+ """
105
+ console.print("\n[cyan]正在测试连接...[/cyan]\n")
106
+
107
+ results = agent.test_connections()
108
+
109
+ all_ok = True
110
+ for service, ok in results.items():
111
+ status = "[green]✓[/green]" if ok else "[red]✗[/red]"
112
+ console.print(f"{status} {service.upper()} 连接")
113
+ all_ok = all_ok and ok
114
+
115
+ console.print()
116
+
117
+ if not all_ok:
118
+ console.print(
119
+ "[yellow]警告:部分服务连接失败,某些功能可能无法使用[/yellow]\n"
120
+ )
121
+
122
+ return all_ok
123
+
124
+
125
+ def main() -> None:
126
+ """Main entry point for CLI."""
127
+ try:
128
+ # Load settings
129
+ settings = get_settings()
130
+
131
+ # Print welcome
132
+ print_welcome()
133
+
134
+ # Initialize agent
135
+ console.print("[dim]正在初始化 Advisor Agent...[/dim]")
136
+ agent = AdvisorAgent(settings)
137
+
138
+ # Test connections
139
+ test_connections(agent)
140
+
141
+ # Start chat loop
142
+ console.print("[dim]准备就绪!开始对话:[/dim]\n")
143
+ run_chat_loop(agent)
144
+
145
+ except KeyboardInterrupt:
146
+ console.print("\n\n[dim]程序被中断[/dim]\n")
147
+ sys.exit(0)
148
+
149
+ except Exception as e:
150
+ console.print(f"\n[red bold]错误:{str(e)}[/red bold]\n")
151
+ logging.error(f"Fatal error: {e}", exc_info=True)
152
+ sys.exit(1)
153
+
154
+
155
+ if __name__ == "__main__":
156
+ main()
@@ -0,0 +1,4 @@
1
+ """Version information for asktable-advisor."""
2
+
3
+ __version__ = "1.0.0"
4
+ __version_info__ = tuple(int(x) for x in __version__.split("."))
@@ -0,0 +1,6 @@
1
+ """AskTable Advisor Agent module."""
2
+
3
+ from .advisor import AdvisorAgent
4
+ from .llm_client import LLMClient
5
+
6
+ __all__ = ["AdvisorAgent", "LLMClient"]
@@ -0,0 +1,337 @@
1
+ """Main Advisor Agent with conversational AI and tool execution."""
2
+
3
+ import json
4
+ import logging
5
+ from typing import Any, Dict, List, Optional
6
+
7
+ from anthropic.types import Message, MessageParam
8
+
9
+ from ..config import AdvisorSettings
10
+ from ..asktable.client import AskTableClient
11
+ from ..asktable.inspector import AskTableInspector
12
+ from ..database.manager import DatabaseManager
13
+ from ..database.schema_generator import SchemaGenerator
14
+ from ..database.data_generator import DataGenerator
15
+ from .llm_client import LLMClient
16
+ from .tools import get_tools
17
+ from .prompts import get_system_prompt
18
+
19
+ logger = logging.getLogger(__name__)
20
+
21
+
22
+ class AdvisorAgent:
23
+ """
24
+ AskTable Advisor AI Agent.
25
+
26
+ Conversational agent that helps users create and manage AskTable scenarios.
27
+ """
28
+
29
+ def __init__(self, settings: AdvisorSettings):
30
+ """
31
+ Initialize Advisor Agent.
32
+
33
+ Args:
34
+ settings: Application settings
35
+ """
36
+ self.settings = settings
37
+
38
+ # Initialize components
39
+ self.llm_client = LLMClient(settings)
40
+ self.db_manager = DatabaseManager(settings)
41
+ self.asktable_client = AskTableClient(
42
+ api_key=settings.asktable_api_key,
43
+ base_url=settings.asktable_api_base,
44
+ )
45
+ self.asktable_inspector = AskTableInspector(self.asktable_client)
46
+
47
+ # Generators
48
+ self.schema_generator = SchemaGenerator(self.llm_client)
49
+ self.data_generator = DataGenerator(self.llm_client)
50
+
51
+ # Conversation state
52
+ self.conversation_history: List[MessageParam] = []
53
+ self.system_prompt = get_system_prompt()
54
+ self.tools = get_tools()
55
+
56
+ logger.info("Advisor Agent initialized")
57
+
58
+ def chat(self, user_message: str) -> str:
59
+ """
60
+ Process user message and return response.
61
+
62
+ Args:
63
+ user_message: User's input message
64
+
65
+ Returns:
66
+ Agent's response text
67
+ """
68
+ # Add user message to history
69
+ self.conversation_history.append({
70
+ "role": "user",
71
+ "content": user_message,
72
+ })
73
+
74
+ # Process with LLM and tools
75
+ response_text = self._process_turn()
76
+
77
+ return response_text
78
+
79
+ def _process_turn(self) -> str:
80
+ """Process one conversation turn with potential tool use."""
81
+ max_iterations = 10 # Prevent infinite loops
82
+ iteration = 0
83
+
84
+ while iteration < max_iterations:
85
+ iteration += 1
86
+
87
+ # Call LLM
88
+ response = self.llm_client.create_message(
89
+ messages=self.conversation_history,
90
+ system=self.system_prompt,
91
+ tools=self.tools,
92
+ )
93
+
94
+ # Check if LLM wants to use tools
95
+ if self.llm_client.has_tool_use(response):
96
+ # Execute tools and continue
97
+ self._execute_tools(response)
98
+ else:
99
+ # LLM has final response
100
+ response_text = self.llm_client.extract_text(response)
101
+
102
+ # Add assistant response to history
103
+ self.conversation_history.append({
104
+ "role": "assistant",
105
+ "content": response_text,
106
+ })
107
+
108
+ return response_text
109
+
110
+ # Max iterations reached
111
+ error_msg = "对话迭代次数过多,请重新开始"
112
+ logger.warning(f"Max iterations reached in conversation")
113
+ return error_msg
114
+
115
+ def _execute_tools(self, message: Message) -> None:
116
+ """
117
+ Execute tools requested by LLM.
118
+
119
+ Args:
120
+ message: LLM message containing tool use blocks
121
+ """
122
+ # Add assistant message with tool use to history
123
+ self.conversation_history.append({
124
+ "role": "assistant",
125
+ "content": message.content,
126
+ })
127
+
128
+ # Extract and execute each tool
129
+ tool_uses = self.llm_client.extract_tool_uses(message)
130
+
131
+ for tool_use in tool_uses:
132
+ tool_name = tool_use.name
133
+ tool_input = tool_use.input
134
+ tool_use_id = tool_use.id
135
+
136
+ logger.info(f"Executing tool: {tool_name}")
137
+
138
+ try:
139
+ # Execute tool
140
+ result = self._execute_tool(tool_name, tool_input)
141
+
142
+ # Format result
143
+ tool_result = self.llm_client.format_tool_result(
144
+ tool_use_id=tool_use_id,
145
+ content=result,
146
+ is_error=False,
147
+ )
148
+
149
+ except Exception as e:
150
+ logger.error(f"Tool execution failed: {e}")
151
+ tool_result = self.llm_client.format_tool_result(
152
+ tool_use_id=tool_use_id,
153
+ content=f"工具执行失败:{str(e)}",
154
+ is_error=True,
155
+ )
156
+
157
+ # Add tool result to history
158
+ self.conversation_history.append({
159
+ "role": "user",
160
+ "content": [tool_result],
161
+ })
162
+
163
+ def _execute_tool(self, tool_name: str, tool_input: Dict[str, Any]) -> Any:
164
+ """
165
+ Execute a specific tool.
166
+
167
+ Args:
168
+ tool_name: Name of the tool to execute
169
+ tool_input: Tool input parameters
170
+
171
+ Returns:
172
+ Tool execution result
173
+ """
174
+ # Dispatch to appropriate handler
175
+ if tool_name == "inspect_asktable_project":
176
+ return self._tool_inspect_project(tool_input)
177
+
178
+ elif tool_name == "design_database_schema":
179
+ return self._tool_design_schema(tool_input)
180
+
181
+ elif tool_name == "generate_sample_data":
182
+ return self._tool_generate_data(tool_input)
183
+
184
+ elif tool_name == "execute_sql":
185
+ return self._tool_execute_sql(tool_input)
186
+
187
+ elif tool_name == "create_asktable_datasource":
188
+ return self._tool_create_datasource(tool_input)
189
+
190
+ elif tool_name == "create_asktable_bot":
191
+ return self._tool_create_bot(tool_input)
192
+
193
+ elif tool_name == "create_asktable_chat":
194
+ return self._tool_create_chat(tool_input)
195
+
196
+ elif tool_name == "enhance_bot":
197
+ return self._tool_enhance_bot(tool_input)
198
+
199
+ elif tool_name == "create_permissions":
200
+ return self._tool_create_permissions(tool_input)
201
+
202
+ elif tool_name == "ask_user":
203
+ return self._tool_ask_user(tool_input)
204
+
205
+ else:
206
+ raise ValueError(f"Unknown tool: {tool_name}")
207
+
208
+ # Tool implementations
209
+ def _tool_inspect_project(self, params: Dict[str, Any]) -> Dict[str, Any]:
210
+ """Inspect AskTable project."""
211
+ detailed = params.get("detailed", False)
212
+ return self.asktable_inspector.inspect_project(detailed=detailed)
213
+
214
+ def _tool_design_schema(self, params: Dict[str, Any]) -> str:
215
+ """Design database schema."""
216
+ return self.schema_generator.generate_schema(
217
+ scenario_name=params["scenario_name"],
218
+ scenario_description=params["scenario_description"],
219
+ requirements=params.get("requirements"),
220
+ scale_info=params.get("scale_info"),
221
+ )
222
+
223
+ def _tool_generate_data(self, params: Dict[str, Any]) -> str:
224
+ """Generate sample data."""
225
+ return self.data_generator.generate_data(
226
+ schema_sql=params["schema_sql"],
227
+ scenario_context=params.get("scenario_context", ""),
228
+ data_volume=params["data_volume"],
229
+ )
230
+
231
+ def _tool_execute_sql(self, params: Dict[str, Any]) -> Dict[str, Any]:
232
+ """Execute SQL statements."""
233
+ sql = params["sql"]
234
+ description = params.get("description", "执行 SQL")
235
+
236
+ count = self.db_manager.execute_sql(sql, description)
237
+
238
+ return {
239
+ "success": True,
240
+ "statements_executed": count,
241
+ "message": f"成功执行 {count} 条 SQL 语句",
242
+ }
243
+
244
+ def _tool_create_datasource(self, params: Dict[str, Any]) -> Dict[str, Any]:
245
+ """Create AskTable datasource."""
246
+ datasource = self.asktable_client.create_datasource(
247
+ name=params["name"],
248
+ database_name=params["database_name"],
249
+ description=params.get("description"),
250
+ )
251
+ return datasource
252
+
253
+ def _tool_create_bot(self, params: Dict[str, Any]) -> Dict[str, Any]:
254
+ """Create AskTable bot."""
255
+ bot = self.asktable_client.create_bot(
256
+ name=params["name"],
257
+ datasource_id=params["datasource_id"],
258
+ description=params.get("description"),
259
+ instructions=params.get("instructions"),
260
+ sample_questions=params.get("sample_questions"),
261
+ )
262
+ return bot
263
+
264
+ def _tool_create_chat(self, params: Dict[str, Any]) -> Dict[str, Any]:
265
+ """Create AskTable chat."""
266
+ chat = self.asktable_client.create_chat(
267
+ bot_id=params["bot_id"],
268
+ name=params["name"],
269
+ )
270
+ return chat
271
+
272
+ def _tool_enhance_bot(self, params: Dict[str, Any]) -> Dict[str, Any]:
273
+ """Enhance existing bot."""
274
+ bot_id = params["bot_id"]
275
+ action = params["action"]
276
+ content = params.get("content", {})
277
+
278
+ if action == "add_questions":
279
+ # Get current bot
280
+ bot = self.asktable_client.get_bot(bot_id)
281
+ if not bot:
282
+ raise ValueError(f"Bot not found: {bot_id}")
283
+
284
+ # Add new questions
285
+ current_questions = bot.get("sample_questions", [])
286
+ new_questions = content.get("questions", [])
287
+ all_questions = current_questions + new_questions
288
+
289
+ # Update bot
290
+ updated_bot = self.asktable_client.update_bot(
291
+ bot_id=bot_id,
292
+ sample_questions=all_questions,
293
+ )
294
+ return updated_bot
295
+
296
+ elif action == "update_instructions":
297
+ updated_bot = self.asktable_client.update_bot(
298
+ bot_id=bot_id,
299
+ instructions=content.get("instructions"),
300
+ )
301
+ return updated_bot
302
+
303
+ else:
304
+ raise ValueError(f"Unknown action: {action}")
305
+
306
+ def _tool_create_permissions(self, params: Dict[str, Any]) -> Dict[str, Any]:
307
+ """Create permissions (placeholder)."""
308
+ # This is a placeholder for future implementation
309
+ return {
310
+ "message": "权限配置功能尚未实现,这是占位符",
311
+ "datasource_id": params.get("datasource_id"),
312
+ "roles": params.get("roles", []),
313
+ }
314
+
315
+ def _tool_ask_user(self, params: Dict[str, Any]) -> str:
316
+ """Ask user a question (returns question to be displayed)."""
317
+ # In CLI mode, this will be handled by the CLI layer
318
+ # For now, just return the question
319
+ question = params["question"]
320
+ options = params.get("options")
321
+
322
+ if options:
323
+ return f"{question}\n选项:{', '.join(options)}"
324
+ return question
325
+
326
+ def reset_conversation(self) -> None:
327
+ """Reset conversation history."""
328
+ self.conversation_history = []
329
+ logger.info("Conversation reset")
330
+
331
+ def test_connections(self) -> Dict[str, bool]:
332
+ """Test all connections."""
333
+ return {
334
+ "llm": self.llm_client.test_connection(),
335
+ "database": self.db_manager.test_connection(),
336
+ "asktable": self.asktable_client.test_connection(),
337
+ }