jarvis-ai-assistant 0.1.220__py3-none-any.whl → 0.1.221__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.
- jarvis/__init__.py +1 -1
- jarvis/jarvis_agent/__init__.py +93 -382
- jarvis/jarvis_agent/edit_file_handler.py +32 -185
- jarvis/jarvis_agent/prompt_builder.py +57 -0
- jarvis/jarvis_agent/prompts.py +188 -0
- jarvis/jarvis_agent/protocols.py +30 -0
- jarvis/jarvis_agent/session_manager.py +84 -0
- jarvis/jarvis_agent/tool_executor.py +49 -0
- jarvis/jarvis_code_agent/code_agent.py +4 -4
- jarvis/jarvis_data/config_schema.json +8 -18
- jarvis/jarvis_rag/__init__.py +2 -2
- jarvis/jarvis_rag/cache.py +28 -30
- jarvis/jarvis_rag/cli.py +141 -52
- jarvis/jarvis_rag/embedding_manager.py +32 -46
- jarvis/jarvis_rag/llm_interface.py +32 -34
- jarvis/jarvis_rag/query_rewriter.py +11 -12
- jarvis/jarvis_rag/rag_pipeline.py +40 -43
- jarvis/jarvis_rag/reranker.py +18 -18
- jarvis/jarvis_rag/retriever.py +29 -29
- jarvis/jarvis_tools/edit_file.py +11 -36
- jarvis/jarvis_utils/config.py +10 -25
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.221.dist-info}/METADATA +15 -12
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.221.dist-info}/RECORD +27 -22
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.221.dist-info}/WHEEL +0 -0
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.221.dist-info}/entry_points.txt +0 -0
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.221.dist-info}/licenses/LICENSE +0 -0
- {jarvis_ai_assistant-0.1.220.dist-info → jarvis_ai_assistant-0.1.221.dist-info}/top_level.txt +0 -0
@@ -3,12 +3,8 @@ import re
|
|
3
3
|
from typing import Any, Dict, List, Tuple
|
4
4
|
|
5
5
|
from jarvis.jarvis_agent.output_handler import OutputHandler
|
6
|
-
from jarvis.jarvis_platform.registry import PlatformRegistry
|
7
|
-
from jarvis.jarvis_utils.git_utils import revert_file
|
8
|
-
from jarvis.jarvis_utils.globals import get_interrupt, set_interrupt
|
9
6
|
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
10
7
|
from jarvis.jarvis_utils.tag import ct, ot
|
11
|
-
from jarvis.jarvis_utils.utils import is_context_overflow
|
12
8
|
|
13
9
|
|
14
10
|
class EditFileHandler(OutputHandler):
|
@@ -69,13 +65,7 @@ class EditFileHandler(OutputHandler):
|
|
69
65
|
]
|
70
66
|
|
71
67
|
print(f"📝 正在处理文件 {file_path}...")
|
72
|
-
# 首先尝试fast_edit模式
|
73
68
|
success, result = self._fast_edit(file_path, file_patches)
|
74
|
-
if not success:
|
75
|
-
# 如果fast_edit失败,尝试slow_edit模式
|
76
|
-
success, result = EditFileHandler._slow_edit(
|
77
|
-
file_path, file_patches, agent
|
78
|
-
)
|
79
69
|
|
80
70
|
if success:
|
81
71
|
results.append(f"✅ 文件 {file_path} 修改成功")
|
@@ -173,7 +163,7 @@ class EditFileHandler(OutputHandler):
|
|
173
163
|
1. 直接进行字符串替换,效率高
|
174
164
|
2. 会自动处理缩进问题,尝试匹配不同缩进级别的代码
|
175
165
|
3. 确保搜索文本在文件中唯一匹配
|
176
|
-
4.
|
166
|
+
4. 如果部分补丁失败,会继续应用剩余补丁,并报告失败信息
|
177
167
|
|
178
168
|
Args:
|
179
169
|
file_path: 要修改的文件路径,支持绝对路径和相对路径
|
@@ -181,8 +171,8 @@ class EditFileHandler(OutputHandler):
|
|
181
171
|
|
182
172
|
Returns:
|
183
173
|
Tuple[bool, str]:
|
184
|
-
|
185
|
-
|
174
|
+
返回处理结果元组,第一个元素表示是否所有补丁都成功应用,
|
175
|
+
第二个元素为结果信息,全部成功时为修改后的文件内容,部分或全部失败时为错误信息
|
186
176
|
"""
|
187
177
|
try:
|
188
178
|
# 确保目录存在
|
@@ -197,13 +187,17 @@ class EditFileHandler(OutputHandler):
|
|
197
187
|
# 应用所有补丁
|
198
188
|
modified_content = file_content
|
199
189
|
patch_count = 0
|
190
|
+
failed_patches: List[Dict[str, Any]] = []
|
191
|
+
successful_patches = 0
|
192
|
+
|
200
193
|
for patch in patches:
|
194
|
+
patch_count += 1
|
201
195
|
search_text = patch["SEARCH"]
|
202
196
|
replace_text = patch["REPLACE"]
|
203
|
-
patch_count += 1
|
204
197
|
|
205
198
|
# 精确匹配搜索文本(保留原始换行和空格)
|
206
199
|
exact_search = search_text
|
200
|
+
found = False
|
207
201
|
|
208
202
|
if exact_search in modified_content:
|
209
203
|
# 直接执行替换(保留所有原始格式)
|
@@ -211,8 +205,8 @@ class EditFileHandler(OutputHandler):
|
|
211
205
|
exact_search, replace_text
|
212
206
|
)
|
213
207
|
print(f"✅ 补丁 #{patch_count} 应用成功")
|
208
|
+
found = True
|
214
209
|
else:
|
215
|
-
found = False
|
216
210
|
# 如果匹配不到,并且search与replace块的首尾都是换行,尝试去掉第一个和最后一个换行
|
217
211
|
if (
|
218
212
|
search_text.startswith("\n")
|
@@ -261,184 +255,37 @@ class EditFileHandler(OutputHandler):
|
|
261
255
|
found = True
|
262
256
|
break
|
263
257
|
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
258
|
+
if found:
|
259
|
+
successful_patches += 1
|
260
|
+
else:
|
261
|
+
error_msg = "搜索文本在文件中不存在"
|
262
|
+
PrettyOutput.print(
|
263
|
+
f"{error_msg}:\n{search_text}",
|
264
|
+
output_type=OutputType.WARNING,
|
265
|
+
)
|
266
|
+
failed_patches.append({"patch": patch, "error": error_msg})
|
270
267
|
|
271
268
|
# 写入修改后的内容
|
272
269
|
with open(file_path, "w", encoding="utf-8") as f:
|
273
270
|
f.write(modified_content)
|
274
271
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
file_path: str, patches: List[Dict[str, str]], agent: Any
|
286
|
-
) -> Tuple[bool, str]:
|
287
|
-
"""使用AI模型生成补丁并应用到文件
|
288
|
-
|
289
|
-
当_fast_edit方法失败时调用此方法,使用AI模型生成更精确的补丁。
|
290
|
-
特点:
|
291
|
-
1. 适用于复杂修改场景或需要上下文理解的修改
|
292
|
-
2. 会自动处理大文件上传问题
|
293
|
-
3. 会尝试最多3次生成有效的补丁
|
294
|
-
4. 生成的补丁会再次通过_fast_edit方法应用
|
295
|
-
5. 如果失败会自动回滚修改
|
296
|
-
|
297
|
-
Args:
|
298
|
-
file_path: 要修改的文件路径,支持绝对路径和相对路径
|
299
|
-
patches: 补丁列表,每个补丁包含search(搜索文本)和replace(替换文本)
|
300
|
-
agent: 执行处理的agent实例,用于访问AI模型平台
|
301
|
-
|
302
|
-
Returns:
|
303
|
-
Tuple[bool, str]:
|
304
|
-
返回处理结果元组,第一个元素表示是否成功(True/False),
|
305
|
-
第二个元素为结果信息,成功时为修改后的文件内容,失败时为错误信息
|
306
|
-
"""
|
307
|
-
try:
|
308
|
-
model = PlatformRegistry().get_normal_platform()
|
309
|
-
|
310
|
-
# 读取原始文件内容
|
311
|
-
file_content = ""
|
312
|
-
if os.path.exists(file_path):
|
313
|
-
with open(file_path, "r", encoding="utf-8") as f:
|
314
|
-
file_content = f.read()
|
315
|
-
|
316
|
-
is_large_context = is_context_overflow(file_content)
|
317
|
-
upload_success = False
|
318
|
-
|
319
|
-
# 如果是大文件,尝试上传到模型平台
|
320
|
-
if (
|
321
|
-
is_large_context
|
322
|
-
and model.support_upload_files()
|
323
|
-
and model.upload_files([file_path])
|
324
|
-
):
|
325
|
-
upload_success = True
|
326
|
-
|
327
|
-
model.set_suppress_output(False)
|
328
|
-
|
329
|
-
# 构建补丁内容
|
330
|
-
patch_content = []
|
331
|
-
for patch in patches:
|
332
|
-
patch_content.append(
|
333
|
-
{
|
334
|
-
"SEARCH": patch["SEARCH"],
|
335
|
-
"REPLACE": patch["REPLACE"],
|
336
|
-
}
|
272
|
+
if failed_patches:
|
273
|
+
error_details = [
|
274
|
+
f" - 失败的补丁: \n{p['patch']['SEARCH']}\n 错误: {p['error']}"
|
275
|
+
for p in failed_patches
|
276
|
+
]
|
277
|
+
summary = (
|
278
|
+
f"文件 {file_path} 修改部分成功。\n"
|
279
|
+
f"成功: {successful_patches}/{patch_count}, "
|
280
|
+
f"失败: {len(failed_patches)}/{patch_count}.\n"
|
281
|
+
f"失败详情:\n" + "\n".join(error_details)
|
337
282
|
)
|
283
|
+
print(f"❌ {summary}")
|
284
|
+
return False, summary
|
338
285
|
|
339
|
-
|
340
|
-
|
341
|
-
# 代码补丁生成专家指南
|
342
|
-
|
343
|
-
## 任务描述
|
344
|
-
你是一位精确的代码补丁生成专家,需要根据补丁描述生成精确的代码差异。
|
345
|
-
|
346
|
-
### 补丁内容
|
347
|
-
```
|
348
|
-
{str(patch_content)}
|
349
|
-
```
|
350
|
-
|
351
|
-
## 补丁生成要求
|
352
|
-
1. **精确性**:严格按照补丁的意图修改代码
|
353
|
-
2. **格式一致性**:严格保持原始代码的格式风格,如果补丁中缩进或者空行与原代码不一致,则需要修正补丁中的缩进或者空行
|
354
|
-
3. **最小化修改**:只修改必要的代码部分,保持其他部分不变
|
355
|
-
4. **上下文完整性**:提供足够的上下文,确保补丁能准确应用
|
356
|
-
|
357
|
-
## 输出格式规范
|
358
|
-
- 使用{ot("DIFF")}块包围每个需要修改的代码段
|
359
|
-
- 每个{ot("DIFF")}块必须包含SEARCH部分和REPLACE部分
|
360
|
-
- SEARCH部分是需要查找的原始代码
|
361
|
-
- REPLACE部分是替换后的新代码
|
362
|
-
- 确保SEARCH部分能在原文件中**唯一匹配**
|
363
|
-
- 如果修改较大,可以使用多个{ot("DIFF")}块
|
364
|
-
|
365
|
-
## 输出模板
|
366
|
-
{ot("DIFF")}
|
367
|
-
{ot("SEARCH")}[需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]{ct("SEARCH")}
|
368
|
-
{ot("REPLACE")}[替换后的新代码]{ct("REPLACE")}
|
369
|
-
{ct("DIFF")}
|
370
|
-
|
371
|
-
{ot("DIFF")}
|
372
|
-
{ot("SEARCH")}[另一处需要查找的原始代码,包含足够上下文,避免出现可匹配多处的情况]{ct("SEARCH")}
|
373
|
-
{ot("REPLACE")}[另一处替换后的新代码]{ct("REPLACE")}
|
374
|
-
{ct("DIFF")}
|
375
|
-
"""
|
376
|
-
|
377
|
-
# 尝试最多3次生成补丁
|
378
|
-
for _ in range(3):
|
379
|
-
if is_large_context:
|
380
|
-
if upload_success:
|
381
|
-
response = model.chat_until_success(main_prompt)
|
382
|
-
else:
|
383
|
-
file_prompt = f"""
|
384
|
-
# 原始代码
|
385
|
-
{file_content}
|
386
|
-
"""
|
387
|
-
response = model.chat_until_success(main_prompt + file_prompt)
|
388
|
-
else:
|
389
|
-
file_prompt = f"""
|
390
|
-
# 原始代码
|
391
|
-
{file_content}
|
392
|
-
"""
|
393
|
-
response = model.chat_until_success(main_prompt + file_prompt)
|
394
|
-
|
395
|
-
# 检查是否被中断
|
396
|
-
if get_interrupt():
|
397
|
-
set_interrupt(False)
|
398
|
-
user_input = agent.multiline_inputer(
|
399
|
-
"补丁应用被中断,请输入补充信息:"
|
400
|
-
)
|
401
|
-
if not user_input.strip():
|
402
|
-
return False, "用户中断了补丁应用"
|
403
|
-
return False, f"用户中断了补丁应用并提供了补充信息: {user_input}"
|
404
|
-
|
405
|
-
# 解析生成的补丁
|
406
|
-
diff_blocks = re.finditer(
|
407
|
-
ot("DIFF")
|
408
|
-
+ r"\s*"
|
409
|
-
+ ot("SEARCH")
|
410
|
-
+ r"(.*?)"
|
411
|
-
+ ct("SEARCH")
|
412
|
-
+ r"\s*"
|
413
|
-
+ ot("REPLACE")
|
414
|
-
+ r"(.*?)"
|
415
|
-
+ ct("REPLACE")
|
416
|
-
+ r"\s*"
|
417
|
-
+ ct("DIFF"),
|
418
|
-
response,
|
419
|
-
re.DOTALL,
|
420
|
-
)
|
421
|
-
|
422
|
-
generated_patches = []
|
423
|
-
for match in diff_blocks:
|
424
|
-
generated_patches.append(
|
425
|
-
{
|
426
|
-
"SEARCH": match.group(1).strip(),
|
427
|
-
"REPLACE": match.group(2).strip(),
|
428
|
-
}
|
429
|
-
)
|
430
|
-
|
431
|
-
if generated_patches:
|
432
|
-
# 尝试应用生成的补丁
|
433
|
-
success, result = EditFileHandler._fast_edit(
|
434
|
-
file_path, generated_patches
|
435
|
-
)
|
436
|
-
if success:
|
437
|
-
return True, result
|
438
|
-
|
439
|
-
return False, "AI模型无法生成有效的补丁"
|
286
|
+
print(f"✅ 文件 {file_path} 修改完成,应用了 {patch_count} 个补丁")
|
287
|
+
return True, modified_content
|
440
288
|
|
441
289
|
except Exception as e:
|
442
290
|
print(f"❌ 文件修改失败: {str(e)}")
|
443
|
-
revert_file(file_path)
|
444
291
|
return False, f"文件修改失败: {str(e)}"
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from typing import List
|
3
|
+
|
4
|
+
from jarvis.jarvis_agent.protocols import OutputHandlerProtocol
|
5
|
+
|
6
|
+
|
7
|
+
def build_action_prompt(output_handlers: List[OutputHandlerProtocol]) -> str:
|
8
|
+
"""
|
9
|
+
Builds the action prompt string from a list of output handlers.
|
10
|
+
|
11
|
+
Args:
|
12
|
+
output_handlers: A list of output handler instances.
|
13
|
+
|
14
|
+
Returns:
|
15
|
+
A formatted string containing the action prompt.
|
16
|
+
"""
|
17
|
+
action_prompt = """
|
18
|
+
<actions>
|
19
|
+
# 🧰 可用操作
|
20
|
+
以下是您可以使用的操作:
|
21
|
+
"""
|
22
|
+
|
23
|
+
# Add tool list overview
|
24
|
+
action_prompt += "\n<overview>\n## Action List\n"
|
25
|
+
action_prompt += (
|
26
|
+
"[" + ", ".join([handler.name() for handler in output_handlers]) + "]"
|
27
|
+
)
|
28
|
+
action_prompt += "\n</overview>"
|
29
|
+
|
30
|
+
# Add details for each tool
|
31
|
+
action_prompt += "\n\n<details>\n# 📝 Action Details\n"
|
32
|
+
for handler in output_handlers:
|
33
|
+
action_prompt += f"\n<tool>\n## {handler.name()}\n"
|
34
|
+
# Get the handler's prompt and ensure correct formatting
|
35
|
+
handler_prompt = handler.prompt().strip()
|
36
|
+
# Adjust indentation to maintain hierarchy
|
37
|
+
handler_prompt = "\n".join(
|
38
|
+
" " + line if line.strip() else line
|
39
|
+
for line in handler_prompt.split("\n")
|
40
|
+
)
|
41
|
+
action_prompt += handler_prompt + "\n</tool>\n"
|
42
|
+
|
43
|
+
# Add tool usage summary
|
44
|
+
action_prompt += """
|
45
|
+
</details>
|
46
|
+
|
47
|
+
<rules>
|
48
|
+
# ❗ 重要操作使用规则
|
49
|
+
1. 一次对话只能使用一个操作,否则会出错
|
50
|
+
2. 严格按照每个操作的格式执行
|
51
|
+
3. 等待操作结果后再进行下一个操作
|
52
|
+
4. 处理完结果后再调用新的操作
|
53
|
+
5. 如果对操作使用不清楚,请请求帮助
|
54
|
+
</rules>
|
55
|
+
</actions>
|
56
|
+
"""
|
57
|
+
return action_prompt
|
@@ -0,0 +1,188 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from jarvis.jarvis_utils.tag import ct, ot
|
3
|
+
|
4
|
+
DEFAULT_SUMMARY_PROMPT = """<report>
|
5
|
+
请生成任务执行的简明总结报告,包括:
|
6
|
+
|
7
|
+
<content>
|
8
|
+
1. 任务目标:任务重述
|
9
|
+
2. 执行结果:成功/失败
|
10
|
+
3. 关键信息:执行过程中提取的重要信息
|
11
|
+
4. 重要发现:任何值得注意的发现
|
12
|
+
5. 后续建议:如果有的话
|
13
|
+
</content>
|
14
|
+
|
15
|
+
<format>
|
16
|
+
请使用简洁的要点描述,突出重要信息。
|
17
|
+
</format>
|
18
|
+
</report>
|
19
|
+
"""
|
20
|
+
|
21
|
+
SUMMARY_REQUEST_PROMPT = """<summary_request>
|
22
|
+
<objective>
|
23
|
+
请对当前对话历史进行简明扼要的总结,提取关键信息和重要决策点。这个总结将作为上下文继续任务,因此需要保留对后续对话至关重要的内容。
|
24
|
+
</objective>
|
25
|
+
|
26
|
+
<guidelines>
|
27
|
+
1. 提取关键信息:任务目标、已确定的事实、重要决策、达成的共识
|
28
|
+
2. 保留技术细节:命令、代码片段、文件路径、配置设置等技术细节
|
29
|
+
3. 记录任务进展:已完成的步骤、当前所处阶段、待解决的问题
|
30
|
+
4. 包含用户偏好:用户表达的明确偏好、限制条件或特殊要求
|
31
|
+
5. 省略冗余内容:问候语、重复信息、不相关的讨论
|
32
|
+
</guidelines>
|
33
|
+
|
34
|
+
<format>
|
35
|
+
- 使用简洁、客观的语言
|
36
|
+
- 按时间顺序或主题组织信息
|
37
|
+
- 使用要点列表增强可读性
|
38
|
+
- 总结应控制在500词以内
|
39
|
+
</format>
|
40
|
+
</summary_request>
|
41
|
+
"""
|
42
|
+
|
43
|
+
|
44
|
+
TASK_ANALYSIS_PROMPT = f"""<task_analysis>
|
45
|
+
<request>
|
46
|
+
当前任务已结束,请分析该任务的解决方案:
|
47
|
+
1. 首先检查现有工具或方法论是否已经可以完成该任务,如果可以,直接说明即可,无需生成新内容
|
48
|
+
2. 如果现有工具/方法论不足,评估当前任务是否可以通过编写新工具来自动化解决
|
49
|
+
3. 如果可以通过工具解决,请设计并提供工具代码
|
50
|
+
4. 如果无法通过编写通用工具完成,评估当前的执行流程是否可以总结为通用方法论
|
51
|
+
5. 如果以上都不可行,给出详细理由
|
52
|
+
请根据分析结果采取相应行动:说明现有工具/方法论、创建新工具、生成新方法论或说明原因。
|
53
|
+
</request>
|
54
|
+
<evaluation_criteria>
|
55
|
+
现有资源评估:
|
56
|
+
1. 现有工具 - 检查系统中是否已有可以完成该任务的工具
|
57
|
+
2. 现有方法论 - 检查是否已有适用于该任务的方法论
|
58
|
+
3. 组合使用 - 评估现有工具和方法论组合使用是否可以解决问题
|
59
|
+
工具评估标准:
|
60
|
+
1. 通用性 - 该工具是否可以解决一类问题,而不仅仅是当前特定问题
|
61
|
+
2. 自动化 - 该工具是否可以减少人工干预,提高效率
|
62
|
+
3. 可靠性 - 该工具是否可以在不同场景下稳定工作
|
63
|
+
4. 简单性 - 该工具是否易于使用,参数设计是否合理
|
64
|
+
方法论评估标准:
|
65
|
+
1. 方法论应聚焦于通用且可重复的解决方案流程
|
66
|
+
2. 方法论应该具备足够的通用性,可应用于同类问题
|
67
|
+
3. 特别注意用户在执行过程中提供的修正、反馈和改进建议
|
68
|
+
4. 如果用户明确指出了某个解决步骤的优化方向,这应该被纳入方法论
|
69
|
+
5. 方法论要严格按照实际的执行流程来总结,不要遗漏或增加任何步骤
|
70
|
+
</evaluation_criteria>
|
71
|
+
<tool_requirements>
|
72
|
+
工具代码要求:
|
73
|
+
1. 工具类名应与工具名称保持一致
|
74
|
+
2. 必须包含name、description、parameters属性
|
75
|
+
3. 必须实现execute方法处理输入参数
|
76
|
+
4. 可选实现check方法验证环境
|
77
|
+
5. 工具描述应详细说明用途、适用场景和使用示例
|
78
|
+
6. 参数定义应遵循JSON Schema格式
|
79
|
+
7. 不要包含特定任务的细节,保持通用性
|
80
|
+
工具设计关键点:
|
81
|
+
1. **使用PrettyOutput打印执行过程**:强烈建议在工具中使用PrettyOutput显示执行过程,
|
82
|
+
这样用户可以了解工具在做什么,提升用户体验。示例:
|
83
|
+
```python
|
84
|
+
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
85
|
+
# 执行中打印信息
|
86
|
+
PrettyOutput.print("正在处理数据...", OutputType.INFO)
|
87
|
+
# 成功信息
|
88
|
+
PrettyOutput.print("操作成功完成", OutputType.SUCCESS)
|
89
|
+
# 警告信息
|
90
|
+
PrettyOutput.print("发现潜在问题", OutputType.WARNING)
|
91
|
+
# 错误信息
|
92
|
+
PrettyOutput.print("操作失败", OutputType.ERROR)
|
93
|
+
```
|
94
|
+
2. **结构化返回结果**:工具应该始终返回结构化的结果字典,包含以下字段:
|
95
|
+
- success: 布尔值,表示操作是否成功
|
96
|
+
- stdout: 字符串,包含工具的主要输出内容
|
97
|
+
- stderr: 字符串,包含错误信息(如果有)
|
98
|
+
3. **异常处理**:工具应该妥善处理可能发生的异常,并在失败时清理已创建的资源
|
99
|
+
```python
|
100
|
+
try:
|
101
|
+
# 执行逻辑
|
102
|
+
return {{
|
103
|
+
"success": True,
|
104
|
+
"stdout": "成功结果",
|
105
|
+
"stderr": ""
|
106
|
+
}}
|
107
|
+
except Exception as e:
|
108
|
+
PrettyOutput.print(f"操作失败: {{str(e)}}", OutputType.ERROR)
|
109
|
+
# 清理资源(如果有创建)
|
110
|
+
return {{
|
111
|
+
"success": False,
|
112
|
+
"stdout": "",
|
113
|
+
"stderr": f"操作失败: {{str(e)}}"
|
114
|
+
}}
|
115
|
+
```
|
116
|
+
</tool_requirements>
|
117
|
+
<methodology_requirements>
|
118
|
+
方法论格式要求:
|
119
|
+
1. 问题重述: 简明扼要的问题归纳,不含特定细节
|
120
|
+
2. 最优解决方案: 经过用户验证的、最终有效的解决方案(将每个步骤要使用的工具也列举出来)
|
121
|
+
3. 注意事项: 执行中可能遇到的常见问题和注意点,尤其是用户指出的问题
|
122
|
+
4. 可选步骤: 对于有多种解决路径的问题,标注出可选步骤和适用场景
|
123
|
+
</methodology_requirements>
|
124
|
+
<output_requirements>
|
125
|
+
根据分析结果,输出以下三种情况之一:
|
126
|
+
1. 如果现有工具/方法论可以解决,直接输出说明:
|
127
|
+
已有工具/方法论可以解决该问题,无需创建新内容。
|
128
|
+
可用的工具/方法论:[列出工具名称或方法论名称]
|
129
|
+
使用方法:[简要说明如何使用]
|
130
|
+
2. 工具创建(如果需要创建新工具):
|
131
|
+
{ot("TOOL_CALL")}
|
132
|
+
want: 创建新工具来解决XXX问题
|
133
|
+
name: generate_new_tool
|
134
|
+
arguments:
|
135
|
+
tool_name: 工具名称
|
136
|
+
tool_code: |2
|
137
|
+
# -*- coding: utf-8 -*-
|
138
|
+
from typing import Dict, Any
|
139
|
+
from jarvis.jarvis_utils.output import PrettyOutput, OutputType
|
140
|
+
class 工具名称:
|
141
|
+
name = "工具名称"
|
142
|
+
description = "Tool for text transformation"
|
143
|
+
Tool description
|
144
|
+
适用场景:1. 格式化文本; 2. 处理标题; 3. 标准化输出
|
145
|
+
\"\"\"
|
146
|
+
parameters = {{
|
147
|
+
"type": "object",
|
148
|
+
"properties": {{
|
149
|
+
# 参数定义
|
150
|
+
}},
|
151
|
+
"required": []
|
152
|
+
}}
|
153
|
+
@staticmethod
|
154
|
+
def check() -> bool:
|
155
|
+
return True
|
156
|
+
def execute(self, args: Dict[str, Any]) -> Dict[str, Any]:
|
157
|
+
try:
|
158
|
+
# 使用PrettyOutput显示执行过程
|
159
|
+
PrettyOutput.print("开始执行操作...", OutputType.INFO)
|
160
|
+
# 实现逻辑
|
161
|
+
# ...
|
162
|
+
PrettyOutput.print("操作已完成", OutputType.SUCCESS)
|
163
|
+
return {{
|
164
|
+
"success": True,
|
165
|
+
"stdout": "结果输出",
|
166
|
+
"stderr": ""
|
167
|
+
}}
|
168
|
+
except Exception as e:
|
169
|
+
PrettyOutput.print(f"操作失败: {{str(e)}}", OutputType.ERROR)
|
170
|
+
return {{
|
171
|
+
"success": False,
|
172
|
+
"stdout": "",
|
173
|
+
"stderr": f"操作失败: {{str(e)}}"
|
174
|
+
}}
|
175
|
+
{ct("TOOL_CALL")}
|
176
|
+
3. 方法论创建(如果需要创建新方法论):
|
177
|
+
{ot("TOOL_CALL")}
|
178
|
+
want: 添加/更新xxxx的方法论
|
179
|
+
name: methodology
|
180
|
+
arguments:
|
181
|
+
operation: add/update
|
182
|
+
problem_type: 方法论类型,不要过于细节,也不要过于泛化
|
183
|
+
content: |2
|
184
|
+
方法论内容
|
185
|
+
{ct("TOOL_CALL")}
|
186
|
+
如果以上三种情况都不适用,则直接输出原因分析,不要使用工具调用格式。
|
187
|
+
</output_requirements>
|
188
|
+
</task_analysis>"""
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
from typing import Any, Protocol, Tuple
|
3
|
+
|
4
|
+
|
5
|
+
class OutputHandlerProtocol(Protocol):
|
6
|
+
"""
|
7
|
+
Defines the interface for an output handler, which is responsible for
|
8
|
+
processing the model's response, typically to execute a tool.
|
9
|
+
"""
|
10
|
+
|
11
|
+
def name(self) -> str:
|
12
|
+
"""Returns the name of the handler."""
|
13
|
+
...
|
14
|
+
|
15
|
+
def can_handle(self, response: str) -> bool:
|
16
|
+
"""Determines if this handler can process the given response."""
|
17
|
+
...
|
18
|
+
|
19
|
+
def prompt(self) -> str:
|
20
|
+
"""Returns the prompt snippet that describes the handler's functionality."""
|
21
|
+
...
|
22
|
+
|
23
|
+
def handle(self, response: str, agent: Any) -> Tuple[bool, Any]:
|
24
|
+
"""
|
25
|
+
Handles the response, executing the associated logic.
|
26
|
+
|
27
|
+
Returns:
|
28
|
+
A tuple containing a boolean (whether to return) and the result.
|
29
|
+
"""
|
30
|
+
...
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
import os
|
3
|
+
from typing import Any, Dict, Optional, TYPE_CHECKING
|
4
|
+
|
5
|
+
from jarvis.jarvis_utils.output import OutputType, PrettyOutput
|
6
|
+
|
7
|
+
if TYPE_CHECKING:
|
8
|
+
from jarvis.jarvis_platform.base import BasePlatform
|
9
|
+
|
10
|
+
|
11
|
+
class SessionManager:
|
12
|
+
"""
|
13
|
+
Manages the session state of an agent, including conversation history,
|
14
|
+
user data, and persistence.
|
15
|
+
"""
|
16
|
+
|
17
|
+
def __init__(self, model: "BasePlatform", agent_name: str):
|
18
|
+
self.model = model
|
19
|
+
self.agent_name = agent_name
|
20
|
+
self.prompt: str = ""
|
21
|
+
self.conversation_length: int = 0
|
22
|
+
self.user_data: Dict[str, Any] = {}
|
23
|
+
self.addon_prompt: str = ""
|
24
|
+
|
25
|
+
def set_user_data(self, key: str, value: Any):
|
26
|
+
"""Sets a value in the user data dictionary."""
|
27
|
+
self.user_data[key] = value
|
28
|
+
|
29
|
+
def get_user_data(self, key: str) -> Optional[Any]:
|
30
|
+
"""Gets a value from the user data dictionary."""
|
31
|
+
return self.user_data.get(key)
|
32
|
+
|
33
|
+
def set_addon_prompt(self, addon_prompt: str):
|
34
|
+
"""Sets the addon prompt for the next model call."""
|
35
|
+
self.addon_prompt = addon_prompt
|
36
|
+
|
37
|
+
def clear(self):
|
38
|
+
"""
|
39
|
+
Clears the current conversation history, prompt, and length counter.
|
40
|
+
"""
|
41
|
+
self.model.reset()
|
42
|
+
self.conversation_length = 0
|
43
|
+
self.prompt = ""
|
44
|
+
|
45
|
+
def save_session(self) -> bool:
|
46
|
+
"""Saves the current session state to a file."""
|
47
|
+
session_dir = os.path.join(os.getcwd(), ".jarvis")
|
48
|
+
os.makedirs(session_dir, exist_ok=True)
|
49
|
+
platform_name = self.model.platform_name()
|
50
|
+
model_name = self.model.name().replace("/", "_").replace("\\", "_")
|
51
|
+
session_file = os.path.join(
|
52
|
+
session_dir,
|
53
|
+
f"saved_session_{self.agent_name}_{platform_name}_{model_name}.json",
|
54
|
+
)
|
55
|
+
return self.model.save(session_file)
|
56
|
+
|
57
|
+
def restore_session(self) -> bool:
|
58
|
+
"""Restores the session state from a file."""
|
59
|
+
platform_name = self.model.platform_name()
|
60
|
+
model_name = self.model.name().replace("/", "_").replace("\\", "_")
|
61
|
+
session_file = os.path.join(
|
62
|
+
os.getcwd(),
|
63
|
+
".jarvis",
|
64
|
+
f"saved_session_{self.agent_name}_{platform_name}_{model_name}.json",
|
65
|
+
)
|
66
|
+
if not os.path.exists(session_file):
|
67
|
+
return False
|
68
|
+
|
69
|
+
if self.model.restore(session_file):
|
70
|
+
try:
|
71
|
+
os.remove(session_file)
|
72
|
+
PrettyOutput.print("会话已恢复,并已删除会话文件。", OutputType.SUCCESS)
|
73
|
+
except OSError as e:
|
74
|
+
PrettyOutput.print(f"删除会话文件失败: {e}", OutputType.ERROR)
|
75
|
+
return True
|
76
|
+
return False
|
77
|
+
|
78
|
+
def clear_history(self):
|
79
|
+
"""
|
80
|
+
Clears conversation history but keeps the system prompt by resetting the model state.
|
81
|
+
"""
|
82
|
+
self.prompt = ""
|
83
|
+
self.model.reset()
|
84
|
+
self.conversation_length = 0
|