逆向api通过外挂解锁特殊功能

逆向api通过外挂解锁特殊功能

很多时候逆向得到的api只能进行文本对话,不能实现最新的ai特性或者协议。比如function call功能 MCP协议 谷歌最新的a2a协议
我经过实践和尝试,提炼了一套可行的思路和代码,使得逆向api一样可以支持最新的ai相关的特性或协议(目前仅仅尝试了function call和mcp,a2a还需要了解)

function call和Mcp的本质以及异同

1.function call

是llm的具体能力,专注于单个外部工具的调用生成

2.Mcp

一种更上层的宏观的系统设计,通常包含多个function call

异同

打个比方,function call是锤子,剪刀等工具,而Mcp就是说明书之类的东西,指导如何用锤子剪刀等工具制造东西
MCP负责决定在什么时候、为什么以及如何组合使用这些“动作”(包括Function Calling、内部思考、信息检索等)来达成最终目标


外挂的实现思路

fc(function call简称)或者Mcp说通俗点就是,llm根据tools参数和上下文判断要不要从上下文中提取参数执行fc或者Mcp.
不管是fc还是Mcp,最终的执行都不是说ai做的,而是ai返回一个结构化(通常是JSON格式)的指令,然后客户端或者别的什么端进行运算.而ai恰好就可以通过**prompt(提示词)**做到这种效果。
因为我们的思路时,在中转或者2api的服务端先过滤判断一次.
如果需要fc或者mcp,通过另外一轮ai对话提炼参数,把返回的参数加入之前的ai对话中,返回进行fc或者Mcp的运算结果.
接收到运算结果后得把结果重新编排并再进行一次对话,添加一个简单的提示词—–比如:根据上下文回答问题

大致思路流程如图

具体实现

需要一个question变量存储用户的问题,一个标志变量tools_use表示是用过了工具
是否需要使用工具我是通过提示词让ai不需要使用工具的时候就输出no

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
# 我的判断是否需要使用工具的提示词
<system>
你是一个专业的 AI 助手,擅长使用各种工具解决问题。你将以专业、友好的态度与用户交流,提供准确和有价值的帮助。
</system>
<persona>

- 专业性:你具备丰富的技术知识和经验
- 思维方式:逻辑清晰,善于分析问题
- 沟通风格:友好、耐心,注重用户体验
- 工作态度:严谨认真,注重细节
</persona>

<instructions>
1. 分析用户需求:
- 仔细理解json格式的工具列表和用户的问题
- 确定需不需要调用工具,需要调用哪些工具以及调用该工具需要的参数

2. 工具使用规范:

- 每次可调用多个工具,如果没有依赖关系,尽量同时返回全部工具的参数,如果有依赖关系,就依次调用
- 如果用户明确要求依次调用才依次调用
- 确保工具调用格式正确,参数完整
3. 回答质量要求:

- 提供清晰、准确、结构化的答案
- 承认知识限制,不随意猜测或编造
- 如果需要使用工具,则返回结果必须严格使用json格式,返回结果不能有其他文字,只返回json格式
- 如果不需要使用工具返回结果不能有其他文字,只返回"no"
4.举例
例子一
用户输入以下内容:
[
{
"type": "function",
"function": {
"name": "fd71edc4f65924613b9fd8330e78eb243",
"description": "获取中国国内的天气预报",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "逗号分隔的经纬度信息 (e.g., 116.40,39.90)"
},
"days": {
"type": "string",
"enum": [
"now",
"24h",
"72h",
"168h",
"3d",
"7d",
"10d",
"15d",
"30d"
],
"description": "预报天数,now为实时天气,24h为24小时预报,72h为72小时预报,168h为168小时预报,3d为3天预报,以此类推"
}
}
}
}
}
]杭州的天气怎么样?
你需要返回的内容是:
[
{
"id": "call_M4eYNv6oPaLunpZLe1iWdfiK",
"function": {
"name": "fd71edc4f65924613b9fd8330e78eb243",
"arguments": "{\"days\":\"now\",\"location\":\"121,31\"}"
},
"type": "function"
}
]
}

例子二
用户输入以下内容:
[
{
"type": "function",
"function": {
"name": "fd71edc4f65924613b9fd8330e78eb243",
"description": "获取中国国内的天气预报",
"parameters": {
"type": "object",
"properties": {
"location": {
"type": "string",
"description": "逗号分隔的经纬度信息 (e.g., 116.40,39.90)"
},
"days": {
"type": "string",
"enum": [
"now",
"24h",
"72h",
"168h",
"3d",
"7d",
"10d",
"15d",
"30d"
],
"description": "预报天数,now为实时天气,24h为24小时预报,72h为72小时预报,168h为168小时预报,3d为3天预报,以此类推"
}
}
}
}
}
]你好
你需要返回的内容是:
no

代码逻辑

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
tools_use = False
tools_answer = 'no'

tools_question = str(tools_json) + messages[-1]["content"] # 缓存用户问题

tools_json = data.get("tools", None) #提取tools参数
# 首先用tools_json和question以及提示词进行新的对话判断是否需要工具
# 需要的话返回的是结构化的调用参数,不需要就返回的no
# 这轮新的对话可以使用2api的模型也可以使用正规的官方api

# 只有tools_answer不是no且tools_use是false时才说需要mcp或者fc且之前没有进行过工具的调用
if tools_answer != 'no' and not tools_use:
# 需要function call
# 构造openai api格式的返回
# 生成created
current_timestamp = int(time.time())
created = current_timestamp
# 下面是告诉客户端需要调用mcp的返回值模板,看了L站n多关于mcp的帖子才知道
completion = {
"id": answer_id,
"object": "chat.completion",
"created": created,
"model": model,
"system_fingerprint": system_fingerprint,
"choices": [{
"index": 0,
"message": {
"role": "assistant",
"tool_calls": json.loads(tools_answer) if tools_answer else None,
"content": None, # 如果有提示词能达到claude使用MCP的思考过程的效果,可以把模拟的思考过程赋值到此处
},
"logprobs": None,
"finish_reason": "tool_call"
}],
"usage": {
"prompt_tokens": prompt_tokens,
"completion_tokens": completion_tokens,
"total_tokens": total_tokens
}
}
tools_answer = 'no' # 重置相关参数,便于下次正确使用



# 判断上下文最后的messages的role角色,如果是tools,说明这是客户端发回来的mcp或者fc调用的结果
elif messages[-1]["role"] == 'tool':

#需要从头到位遍历messages,把tool的role转成user,这样逆向的api才好使用
# tools的结果在content里的第一个的text里面
messages[-1]["content"][0]["text"]


# 同时判断tools_use是不是true, 是说明这就是本次tools的调用结果
if tools_use:
# 这里需要保证对话的最后一条是用户起初提的问题,不同的逆向api这里的代码不一样比如
chat_messages["content"] = question
question = None# 恢复变量和标志位
tools_use = False

MCP的思考过程

我也写了一个粗略的提示词想要达成这种效果,但由于我的提示词功力不够以及逆向的模型过于垃圾,导致效果不理想

end

这样做了以后就可以让逆向api支持mcp协议了
经过openwebui几个简单的工具测试可以达成效果
总结一下,MCP并不是什么困难的东西,只不过批了一层外衣,MCP的模拟过程实质还是用提示词的使用,只要了解了MCP过程中的api返回值接受值的格式就可以轻松地给任意模型加上MCP
未来的llm发展我猜测为两个方向
融合和外挂

融合

就是MOE模型里进一步增加从对话中提取参数结构化输出的能力

外挂

就是主模型增强关于是否需要MCP的判断能力,需要的话交给一个极致轻量的次生模型提取参数并结构化输出。由于次生模型只需要提取参数结构化输出,是可以做到轻量且高效准确的,主模型就可以完全抛弃这部分能力,增强其他能力

补充

下一步了解下谷歌的a2a协议