上次讲解了多模型交互的例子:juejin.cn/post/751876… 本次我们拆分各个的prompt,来看一下各prompt做了哪些事,并结合代码进行梳理。 本次代码全部来自于github.com/yincongcyin…
整体流程概述:任务的智能拆解与执行
该自动化流程的核心在于将一个“主要任务”分解为若干可管理的“子任务”,并为每个子任务匹配最合适的执行者。这个执行者可能是特定的多模态计算平台(MCP)服务,也可能是**大模型(LLM)**本身。整个过程呈现为一个循环迭代的模式,直至所有子任务完成并最终汇总结果。
其具体步骤如下:
- Prompt拆分任务: 流程伊始,系统会接收一个主任务,并利用一个专业的**“深度研究员”角色**的Prompt,将主任务拆解为一系列自动化子任务。这个“深度研究员”的职责是分析主任务,确定“输出专家”生成最终可交付成果所需的所有数据或信息,并设计详细的子任务执行过程。它还会忽略最终输出形式,只专注于数据的收集与信息的提供。
- 子任务分配: 拆分出的每个子任务会根据其需求和MCP服务的不同描述进行智能分配。如果存在适合的MCP服务,则直接分配给该服务;若无匹配,则任务会直接分配给大模型(llm_tool)本身进行处理。
- 大模型功能配置: 针对已分配的子任务,系统会为大模型配置不同的function call(函数调用),以确保大模型能够针对性地处理该子任务,获取所需的数据或信息。
- 循环询问与判断: 子任务完成后,系统会对大模型进行下一轮的询问,以判断是否还有未完成的子任务。这是一个关键的反馈循环机制,确保任务的持续推进。
- 迭代执行: 如果判断有剩余子任务,流程将回到步骤2至4,继续进行子任务的分配、处理和询问。
- 结果总结: 当所有子任务都完成后,流程将进入总结阶段,返回与主要任务相关的最终结果。
核心prompt展示
拆分任务prompt:
角色:\n* 您是一名专业的深度研究员。您的职责是利用一支由专业智能代理组成的团队来规划任务,为“输出专家”收集充分且必要的信息。\n* 输出专家是一名强大的代理,能够生成诸如文档、电子表格、图像、音频等可交付成果。\n\n职责:\n1. 分析主要任务,并确定输出专家生成最终可交付成果所需的所有数据或信息。\n2. 设计一系列自动化子任务,每个子任务都由一个合适的“工作代理”执行。仔细考虑每个步骤的主要目标,并创建一份规划大纲。然后,定义每个子任务的详细执行过程。\n3. 忽略主要任务所需的最终可交付成果:子任务只专注于提供数据或信息,而非生成输出。\n4. 基于主要任务和已完成的子任务,生成或更新您的任务计划。\n5. 判断是否已为输出专家收集到所有必需的信息或数据。\n6. 跟踪任务进度。如果计划需要更新,请避免重复已完成的子任务——只生成剩余的必要子任务。\n7. 如果任务简单且可以直接处理(例如,编写代码、创意写作、基本数据分析或预测),请立即使用 llm_tool,无需进一步规划。\n\n可用工作代理:\n{{range tool := .assign_param}}- 代理名称:{{tool.tool_name}}\n 代理描述:{{tool.tool_desc}}\n{{end}}\n\n主要任务:\n{{.user_task}}\n\n输出格式(JSON):\n\njson\n{\n "plan": [\n {\n "name": "第一个任务所需的代理名称",\n "description": "执行步骤1的详细说明"\n },\n {\n "name": "第二个任务所需的代理名称",\n "description": "执行步骤2的详细说明"\n },\n ...\n ]\n}\n",
他的返回结果
循环任务prompt: 主要任务: {{.user_task}}\n\n已完成的子任务:\n{{range res := .complete_tasks}}\n\t- 子任务:{{$task}}\n{{end}}\n\n当前任务计划:\n{{.last_plan}}\n\n请根据以上信息创建或更新任务计划。如果任务已完成,请返回一个空的计划列表。\n\n注意:\n- 仔细分析上次完成的子任务的完成状态,以确定下一个任务计划。\n- 适当且合理地补充细节,以确保工作代理或工具拥有足够的执行任务的信息。\n- 扩展后的描述不得偏离子任务的主要目标。
可以通过日志看出调用哪些mcp
总结任务prompt: 根据问题,用纯文本格式总结搜索结果和其他参考信息中的要点。\n\n主要任务:\n{{.user_task}}"
deepseek的返回展示:
为何要根据 MCP 服务区分 Function Call?
根据您提供的信息,之所以要根据 MCP(多模态计算平台)服务的不同来区分 Function Call,主要有两个核心原因:
- 避免大模型上下文超限: 大模型(LLM)的请求存在严格的上下文(Context)字数限制。如果将所有的 MCP Function 都直接塞入大模型的请求上下文中,很可能导致上下文长度超出限制,从而无法正常进行处理。
- 优化 Token 使用效率: 将大量 MCP Function 塞入上下文会显著增加 Token 的使用量。Token 是衡量大模型计算成本和效率的关键单位,Token 数量的增加意味着更高的成本和更长的处理时间。通过区分 Function Call,系统可以只将与当前子任务最相关的 Function Call 提供给大模型,从而大幅降低 Token 的消耗,提升整体效率。
简而言之,这种区分 Function Call 的策略是为了在保证大模型处理能力的同时,优化资源使用效率,避免不必要的上下文膨胀和Token浪费。
telegram-deepseek-bot 核心方法详解
ExecuteTask() 方法
func (d *DeepseekTaskReq) ExecuteTask() {
// 设置15分钟超时上下文
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
defer cancel()
// 准备任务参数
taskParam := make(map[string]interface{})
taskParam["assign_param"] = make([]map[string]string, 0)
taskParam["user_task"] = d.Content
// 添加可用工具信息
for name, tool := range conf.TaskTools {
taskParam["assign_param"] = append(taskParam["assign_param"].([]map[string]string), map[string]string{
"tool_name": name,
"tool_desc": tool.Description,
})
}
// 创建LLM客户端
llm := NewLLM(WithBot(d.Bot), WithUpdate(d.Update),
WithMessageChan(d.MessageChan))
// 获取并发送任务分配提示
prompt := i18n.GetMessage(*conf.Lang, "assign_task_prompt", taskParam)
llm.LLMClient.GetUserMessage(prompt)
llm.Content = prompt
// 同步发送请求
c, err := llm.LLMClient.SyncSend(ctx, llm)
if err != nil {
logger.Error("get message fail", "err", err)
return
}
// 解析AI返回的JSON任务计划
matches := jsonRe.FindAllString(c, -1)
plans := new(TaskInfo)
for _, match := range matches {
err = json.Unmarshal([]byte(match), &plans)
if err != nil {
logger.Error("json umarshal fail", "err", err)
}
}
// 如果没有计划,直接请求总结
if len(plans.Plan) == 0 {
finalLLM := NewLLM(WithBot(d.Bot), WithUpdate(d.Update),
WithMessageChan(d.MessageChan), WithContent(d.Content))
finalLLM.LLMClient.GetUserMessage(c)
err = finalLLM.LLMClient.Send(ctx, finalLLM)
return
}
// 执行任务循环
llm.LLMClient.GetAssistantMessage(c)
d.loopTask(ctx, plans, c, llm)
// 最终总结
summaryParam := make(map[string]interface{})
summaryParam["user_task"] = d.Content
llm.LLMClient.GetUserMessage(i18n.GetMessage(*conf.Lang, "summary_task_prompt", summaryParam))
err = llm.LLMClient.Send(ctx, llm)
}
loopTask() 方法
func (d *DeepseekTaskReq) loopTask(ctx context.Context, plans *TaskInfo, lastPlan string, llm *LLM) {
// 记录已完成任务
completeTasks := map[string]bool{}
// 为任务创建专门的LLM实例
taskLLM := NewLLM(WithBot(d.Bot), WithUpdate(d.Update),
WithMessageChan(d.MessageChan))
defer func() {
llm.LLMClient.AppendMessages(taskLLM.LLMClient)
}()
// 执行每个子任务
for _, plan := range plans.Plan {
// 配置任务工具
o := WithTaskTools(conf.TaskTools[plan.Name])
o(taskLLM)
// 发送任务描述
taskLLM.LLMClient.GetUserMessage(plan.Description)
taskLLM.Content = plan.Description
// 执行任务
d.requestTask(ctx, taskLLM, plan)
completeTasks[plan.Description] = true
}
// 准备循环任务参数
taskParam := map[string]interface{}{
"user_task": d.Content,
"complete_tasks": completeTasks,
"last_plan": lastPlan,
}
// 请求AI评估是否需要更多任务
llm.LLMClient.GetUserMessage(i18n.GetMessage(*conf.Lang, "loop_task_prompt", taskParam))
c, err := llm.LLMClient.SyncSend(ctx, llm)
// 解析新任务计划
matches := jsonRe.FindAllString(c, -1)
plans = new(TaskInfo)
for _, match := range matches {
err := json.Unmarshal([]byte(match), &plans)
}
// 如果有新任务,递归调用
if len(plans.Plan) > 0 {
d.loopTask(ctx, plans, c, llm)
}
}
requestTask() 方法
func (d *DeepseekTaskReq) requestTask(ctx context.Context, llm *LLM, plan *Task) {
// 同步发送任务请求
c, err := llm.LLMClient.SyncSend(ctx, llm)
if err != nil {
logger.Error("ChatCompletionStream error", "err", err)
return
}
// 处理空响应
if c == "" {
c = plan.Name + " is completed"
}
// 保存AI响应
llm.LLMClient.GetAssistantMessage(c)
}