iflow-mcp_hanw39_reasoning-bank-mcp 0.2.0__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.
- iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/METADATA +599 -0
- iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/RECORD +55 -0
- iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/WHEEL +4 -0
- iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/entry_points.txt +2 -0
- iflow_mcp_hanw39_reasoning_bank_mcp-0.2.0.dist-info/licenses/LICENSE +21 -0
- src/__init__.py +16 -0
- src/__main__.py +6 -0
- src/config.py +266 -0
- src/deduplication/__init__.py +19 -0
- src/deduplication/base.py +88 -0
- src/deduplication/factory.py +60 -0
- src/deduplication/strategies/__init__.py +1 -0
- src/deduplication/strategies/semantic_dedup.py +187 -0
- src/default_config.yaml +121 -0
- src/initializers/__init__.py +50 -0
- src/initializers/base.py +196 -0
- src/initializers/embedding_initializer.py +22 -0
- src/initializers/llm_initializer.py +22 -0
- src/initializers/memory_manager_initializer.py +55 -0
- src/initializers/retrieval_initializer.py +32 -0
- src/initializers/storage_initializer.py +22 -0
- src/initializers/tools_initializer.py +48 -0
- src/llm/__init__.py +10 -0
- src/llm/base.py +61 -0
- src/llm/factory.py +75 -0
- src/llm/providers/__init__.py +12 -0
- src/llm/providers/anthropic.py +62 -0
- src/llm/providers/dashscope.py +76 -0
- src/llm/providers/openai.py +76 -0
- src/merge/__init__.py +22 -0
- src/merge/base.py +89 -0
- src/merge/factory.py +60 -0
- src/merge/strategies/__init__.py +1 -0
- src/merge/strategies/llm_merge.py +170 -0
- src/merge/strategies/voting_merge.py +108 -0
- src/prompts/__init__.py +21 -0
- src/prompts/formatters.py +74 -0
- src/prompts/templates.py +184 -0
- src/retrieval/__init__.py +8 -0
- src/retrieval/base.py +37 -0
- src/retrieval/factory.py +55 -0
- src/retrieval/strategies/__init__.py +8 -0
- src/retrieval/strategies/cosine_retrieval.py +47 -0
- src/retrieval/strategies/hybrid_retrieval.py +155 -0
- src/server.py +306 -0
- src/services/__init__.py +5 -0
- src/services/memory_manager.py +403 -0
- src/storage/__init__.py +45 -0
- src/storage/backends/json_backend.py +290 -0
- src/storage/base.py +150 -0
- src/tools/__init__.py +8 -0
- src/tools/extract_memory.py +285 -0
- src/tools/retrieve_memory.py +139 -0
- src/utils/__init__.py +7 -0
- src/utils/similarity.py +54 -0
src/server.py
ADDED
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""ReasoningBank MCP 服务器"""
|
|
2
|
+
import asyncio
|
|
3
|
+
import logging
|
|
4
|
+
import argparse
|
|
5
|
+
from typing import Any
|
|
6
|
+
from mcp.server import Server
|
|
7
|
+
from mcp.server.stdio import stdio_server
|
|
8
|
+
from mcp.server.sse import SseServerTransport
|
|
9
|
+
from mcp.types import Tool, TextContent
|
|
10
|
+
from starlette.applications import Starlette
|
|
11
|
+
from starlette.routing import Route, Mount
|
|
12
|
+
from starlette.responses import Response
|
|
13
|
+
import uvicorn
|
|
14
|
+
|
|
15
|
+
from .config import load_config
|
|
16
|
+
from .initializers import (
|
|
17
|
+
InitializerRegistry,
|
|
18
|
+
StorageInitializer,
|
|
19
|
+
LLMInitializer,
|
|
20
|
+
EmbeddingInitializer,
|
|
21
|
+
RetrievalInitializer,
|
|
22
|
+
MemoryManagerInitializer,
|
|
23
|
+
ToolsInitializer
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
# 配置日志
|
|
27
|
+
logging.basicConfig(
|
|
28
|
+
level=logging.INFO,
|
|
29
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
30
|
+
)
|
|
31
|
+
logger = logging.getLogger("reasoning-bank-mcp")
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ReasoningBankServer:
|
|
35
|
+
"""ReasoningBank MCP 服务器"""
|
|
36
|
+
|
|
37
|
+
def __init__(self):
|
|
38
|
+
self.config = None
|
|
39
|
+
self.storage = None
|
|
40
|
+
self.llm = None
|
|
41
|
+
self.embedding = None
|
|
42
|
+
self.retrieval = None
|
|
43
|
+
self.memory_manager = None
|
|
44
|
+
self.retrieve_tool = None
|
|
45
|
+
self.extract_tool = None
|
|
46
|
+
self.server = Server("reasoning-bank")
|
|
47
|
+
async def initialize(self):
|
|
48
|
+
"""初始化服务器组件(使用自动化初始化器架构)"""
|
|
49
|
+
try:
|
|
50
|
+
# 1. 加载配置
|
|
51
|
+
logger.info("正在加载配置...")
|
|
52
|
+
self.config = load_config()
|
|
53
|
+
|
|
54
|
+
# 2. 创建初始化器注册表
|
|
55
|
+
registry = InitializerRegistry()
|
|
56
|
+
|
|
57
|
+
# 3. 自动注册所有组件初始化器
|
|
58
|
+
# 新增组件只需在此列表中添加对应的初始化器类即可
|
|
59
|
+
registry.auto_register([
|
|
60
|
+
StorageInitializer,
|
|
61
|
+
LLMInitializer,
|
|
62
|
+
EmbeddingInitializer,
|
|
63
|
+
RetrievalInitializer,
|
|
64
|
+
MemoryManagerInitializer,
|
|
65
|
+
ToolsInitializer,
|
|
66
|
+
], self.config)
|
|
67
|
+
|
|
68
|
+
# 4. 按依赖顺序自动初始化所有组件
|
|
69
|
+
components = await registry.initialize_all(self.config)
|
|
70
|
+
|
|
71
|
+
# 5. 将组件赋值到实例属性
|
|
72
|
+
self.storage = components.get("storage")
|
|
73
|
+
self.llm = components.get("llm")
|
|
74
|
+
self.embedding = components.get("embedding")
|
|
75
|
+
self.retrieval = components.get("retrieval")
|
|
76
|
+
self.memory_manager = components.get("memory_manager")
|
|
77
|
+
|
|
78
|
+
# tools 返回的是字典
|
|
79
|
+
tools = components.get("tools", {})
|
|
80
|
+
self.retrieve_tool = tools.get("retrieve_tool")
|
|
81
|
+
self.extract_tool = tools.get("extract_tool")
|
|
82
|
+
|
|
83
|
+
# 6. 输出初始化总结
|
|
84
|
+
logger.info("✓ 服务器组件初始化完成")
|
|
85
|
+
logger.info(f" - LLM: {self.llm.get_provider_name()}")
|
|
86
|
+
logger.info(f" - Embedding: {self.embedding.get_provider_name()}")
|
|
87
|
+
logger.info(f" - 检索策略: {self.retrieval.get_name()}")
|
|
88
|
+
storage_config = self.config.get_storage_config()
|
|
89
|
+
logger.info(f" - 存储后端: {storage_config.get('backend', 'json')}")
|
|
90
|
+
if self.memory_manager:
|
|
91
|
+
logger.info(f" - 记忆管理: 已启用")
|
|
92
|
+
else:
|
|
93
|
+
logger.info(f" - 记忆管理: 已禁用")
|
|
94
|
+
|
|
95
|
+
except Exception as e:
|
|
96
|
+
logger.error(f"✗ 服务器初始化失败: {e}")
|
|
97
|
+
raise
|
|
98
|
+
|
|
99
|
+
def setup_handlers(self):
|
|
100
|
+
"""设置 MCP 处理器"""
|
|
101
|
+
# todo 描述得强制些吧 JSON是不是得放在相对路径(2)完善工具描述
|
|
102
|
+
@self.server.list_tools()
|
|
103
|
+
async def list_tools() -> list[Tool]:
|
|
104
|
+
"""列出可用的工具"""
|
|
105
|
+
return [
|
|
106
|
+
Tool(
|
|
107
|
+
name="retrieve_memory",
|
|
108
|
+
description="检索相关的历史经验记忆,帮助指导当前任务的执行。在开始执行任何复杂任务前都应该先调用此工具来获取相关经验。例如:编写代码、数据分析、制定计划等任务前。",
|
|
109
|
+
inputSchema={
|
|
110
|
+
"type": "object",
|
|
111
|
+
"properties": {
|
|
112
|
+
"query": {
|
|
113
|
+
"type": "string",
|
|
114
|
+
"description": "当前任务的查询描述"
|
|
115
|
+
},
|
|
116
|
+
"top_k": {
|
|
117
|
+
"type": "number",
|
|
118
|
+
"description": "检索的记忆数量(可选,默认1)",
|
|
119
|
+
"default": 1
|
|
120
|
+
},
|
|
121
|
+
"agent_id": {
|
|
122
|
+
"type": "string",
|
|
123
|
+
"description": "Agent ID(可选)。用于多租户隔离,只检索指定 agent 的记忆。不提供时检索所有记忆。建议 SubAgent 传递自己的 name 作为 agent_id。"
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
"required": ["query"]
|
|
127
|
+
}
|
|
128
|
+
),
|
|
129
|
+
Tool(
|
|
130
|
+
name="extract_memory",
|
|
131
|
+
description="从任务轨迹中提取推理经验并保存到记忆库。当任务执行完成后必须调用此工具,以便将经验保存供未来使用。这应该在每次任务结束时调用。",
|
|
132
|
+
inputSchema={
|
|
133
|
+
"type": "object",
|
|
134
|
+
"properties": {
|
|
135
|
+
"trajectory": {
|
|
136
|
+
"type": "array",
|
|
137
|
+
"description": "任务执行的轨迹步骤列表,可以在metadata中放入tool名称",
|
|
138
|
+
"items": {
|
|
139
|
+
"type": "object",
|
|
140
|
+
"properties": {
|
|
141
|
+
"step": {"type": "number"},
|
|
142
|
+
"role": {"type": "string", "enum": ["user", "assistant", "tool"]},
|
|
143
|
+
"content": {"type": "string"},
|
|
144
|
+
"metadata": {"type": "object"}
|
|
145
|
+
},
|
|
146
|
+
"required": ["step", "role", "content"]
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
"query": {
|
|
150
|
+
"type": "string",
|
|
151
|
+
"description": "任务查询描述"
|
|
152
|
+
},
|
|
153
|
+
"success_signal": {
|
|
154
|
+
"type": "boolean",
|
|
155
|
+
"description": "任务是否成功(可选,null时自动判断)"
|
|
156
|
+
},
|
|
157
|
+
"async_mode": {
|
|
158
|
+
"type": "boolean",
|
|
159
|
+
"description": "是否异步处理(可选,默认true)"
|
|
160
|
+
},
|
|
161
|
+
"agent_id": {
|
|
162
|
+
"type": "string",
|
|
163
|
+
"description": "Agent ID(可选)。用于多租户隔离,标记记忆属于哪个 agent。建议 SubAgent 传递自己的 name 作为 agent_id。"
|
|
164
|
+
}
|
|
165
|
+
},
|
|
166
|
+
"required": ["trajectory", "query"]
|
|
167
|
+
}
|
|
168
|
+
)
|
|
169
|
+
]
|
|
170
|
+
|
|
171
|
+
@self.server.call_tool()
|
|
172
|
+
async def call_tool(name: str, arguments: Any) -> list[TextContent]:
|
|
173
|
+
"""调用工具"""
|
|
174
|
+
try:
|
|
175
|
+
if name == "retrieve_memory":
|
|
176
|
+
result = await self.retrieve_tool.execute(
|
|
177
|
+
query=arguments["query"],
|
|
178
|
+
top_k=arguments.get("top_k"),
|
|
179
|
+
agent_id=arguments.get("agent_id")
|
|
180
|
+
)
|
|
181
|
+
elif name == "extract_memory":
|
|
182
|
+
result = await self.extract_tool.execute(
|
|
183
|
+
trajectory=arguments["trajectory"],
|
|
184
|
+
query=arguments["query"],
|
|
185
|
+
success_signal=arguments.get("success_signal"),
|
|
186
|
+
async_mode=arguments.get("async_mode"),
|
|
187
|
+
agent_id=arguments.get("agent_id")
|
|
188
|
+
)
|
|
189
|
+
else:
|
|
190
|
+
result = {"status": "error", "message": f"Unknown tool: {name}"}
|
|
191
|
+
|
|
192
|
+
# 返回格式化的结果
|
|
193
|
+
import json
|
|
194
|
+
return [TextContent(
|
|
195
|
+
type="text",
|
|
196
|
+
text=json.dumps(result, ensure_ascii=False, indent=2)
|
|
197
|
+
)]
|
|
198
|
+
|
|
199
|
+
except Exception as e:
|
|
200
|
+
logger.error(f"工具调用失败 [{name}]: {e}", exc_info=True)
|
|
201
|
+
return [TextContent(
|
|
202
|
+
type="text",
|
|
203
|
+
text=json.dumps({
|
|
204
|
+
"status": "error",
|
|
205
|
+
"message": f"工具执行出错: {str(e)}"
|
|
206
|
+
}, ensure_ascii=False, indent=2)
|
|
207
|
+
)]
|
|
208
|
+
|
|
209
|
+
async def run_stdio(self):
|
|
210
|
+
"""使用 STDIO 运行服务器"""
|
|
211
|
+
logger.info("正在启动 ReasoningBank MCP 服务器 (STDIO 模式)...")
|
|
212
|
+
|
|
213
|
+
# 初始化组件
|
|
214
|
+
await self.initialize()
|
|
215
|
+
|
|
216
|
+
# 设置处理器
|
|
217
|
+
self.setup_handlers()
|
|
218
|
+
|
|
219
|
+
# 启动服务器
|
|
220
|
+
logger.info("✓ 服务器已启动,等待连接...")
|
|
221
|
+
async with stdio_server() as (read_stream, write_stream):
|
|
222
|
+
await self.server.run(
|
|
223
|
+
read_stream,
|
|
224
|
+
write_stream,
|
|
225
|
+
self.server.create_initialization_options()
|
|
226
|
+
)
|
|
227
|
+
|
|
228
|
+
async def run_sse(self, host: str = "127.0.0.1", port: int = 8000):
|
|
229
|
+
"""使用 SSE 运行服务器"""
|
|
230
|
+
logger.info(f"正在启动 ReasoningBank MCP 服务器 (SSE 模式) 在 {host}:{port}...")
|
|
231
|
+
|
|
232
|
+
# 初始化组件
|
|
233
|
+
await self.initialize()
|
|
234
|
+
|
|
235
|
+
# 设置处理器
|
|
236
|
+
self.setup_handlers()
|
|
237
|
+
|
|
238
|
+
# 创建 SSE 传输层
|
|
239
|
+
sse = SseServerTransport("/messages/")
|
|
240
|
+
|
|
241
|
+
async def handle_sse(request):
|
|
242
|
+
"""处理 SSE 连接"""
|
|
243
|
+
async with sse.connect_sse(
|
|
244
|
+
request.scope, request.receive, request._send
|
|
245
|
+
) as streams:
|
|
246
|
+
await self.server.run(
|
|
247
|
+
streams[0], streams[1], self.server.create_initialization_options()
|
|
248
|
+
)
|
|
249
|
+
return Response()
|
|
250
|
+
|
|
251
|
+
# 创建 Starlette 应用
|
|
252
|
+
app = Starlette(
|
|
253
|
+
routes=[
|
|
254
|
+
Route("/sse", endpoint=handle_sse, methods=["GET"]),
|
|
255
|
+
Mount("/messages/", app=sse.handle_post_message),
|
|
256
|
+
]
|
|
257
|
+
)
|
|
258
|
+
|
|
259
|
+
logger.info("✓ 服务器已启动,等待连接...")
|
|
260
|
+
logger.info(f" SSE 端点: http://{host}:{port}/sse")
|
|
261
|
+
|
|
262
|
+
# 运行服务器
|
|
263
|
+
config = uvicorn.Config(app, host=host, port=port, log_level="info")
|
|
264
|
+
server = uvicorn.Server(config)
|
|
265
|
+
await server.serve()
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
async def main():
|
|
269
|
+
"""主函数"""
|
|
270
|
+
parser = argparse.ArgumentParser(description="ReasoningBank MCP 服务器")
|
|
271
|
+
parser.add_argument(
|
|
272
|
+
"--transport",
|
|
273
|
+
choices=["stdio", "sse"],
|
|
274
|
+
default="stdio",
|
|
275
|
+
help="传输方式: stdio (默认) 或 sse"
|
|
276
|
+
)
|
|
277
|
+
parser.add_argument(
|
|
278
|
+
"--host",
|
|
279
|
+
default="127.0.0.1",
|
|
280
|
+
help="SSE 模式的主机地址 (默认: 127.0.0.1)"
|
|
281
|
+
)
|
|
282
|
+
parser.add_argument(
|
|
283
|
+
"--port",
|
|
284
|
+
type=int,
|
|
285
|
+
default=8000,
|
|
286
|
+
help="SSE 模式的端口号 (默认: 8000)"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
args = parser.parse_args()
|
|
290
|
+
|
|
291
|
+
server = ReasoningBankServer()
|
|
292
|
+
|
|
293
|
+
if args.transport == "stdio":
|
|
294
|
+
await server.run_stdio()
|
|
295
|
+
else: # sse
|
|
296
|
+
await server.run_sse(host=args.host, port=args.port)
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def run_server():
|
|
300
|
+
"""运行服务器的同步入口点"""
|
|
301
|
+
import asyncio
|
|
302
|
+
asyncio.run(main())
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
if __name__ == "__main__":
|
|
306
|
+
run_server()
|