dify 解析笔记-工具篇
- 入口
选择工具duckduckgo
发送消息后,后台的接口:
1.chat-messages:https://cloud.dify.ai/console/api/apps/a21e7956-378f-47ea-9ce6-3d390d4674b4/chat-messages
载荷
{"response_mode":"streaming","conversation_id":"","query":"hello","inputs":{},"model_config":{"pre_prompt":"","prompt_type":"simple","chat_prompt_config":{},"completion_prompt_config":{},"user_input_form":[],"dataset_query_variable":"","opening_statement":null,"more_like_this":{"enabled":false},"suggested_questions":[],"suggested_questions_after_answer":{"enabled":false},"text_to_speech":{"enabled":false},"speech_to_text":{"enabled":false},"retriever_resource":{"enabled":true},"sensitive_word_avoidance":{"enabled":false,"type":"","configs":[]},"agent_mode":{"max_iteration":5,"enabled":true,"strategy":"function_call","tools":[{"provider_id":"duckduckgo","provider_type":"builtin","provider_name":"duckduckgo","tool_name":"ddgo_ai","tool_label":"DuckDuckGo AI聊天","tool_parameters":{"query":"","model":""},"enabled":true}],"prompt":null},"dataset_configs":{"retrieval_model":"multiple","datasets":{"datasets":[]}},"file_upload":{"image":{"enabled":false,"number_limits":3,"detail":"high","transfer_methods":["remote_url","local_file"]}},"annotation_reply":{"enabled":false},"supportAnnotation":true,"appId":"a21e7956-378f-47ea-9ce6-3d390d4674b4","supportCitationHitInfo":true,"model":{"provider":"openai","name":"gpt-4o-mini","mode":"chat","completion_params":{}}}}
响应结果
data: {"event": "agent_thought", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "7409b3c5-8952-498e-bd9d-e36a168a53e6", "position": 1, "thought": "", "observation": "", "tool": "", "tool_labels": {}, "tool_input": "", "message_files": []}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": "Hello"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": "!"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": " How"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": " can"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": " I"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": " assist"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": " you"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": " today"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": "?"}
data: {"event": "agent_message", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "answer": ""}
data: {"event": "agent_thought", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "7409b3c5-8952-498e-bd9d-e36a168a53e6", "position": 1, "thought": "Hello! How can I assist you today?", "observation": "", "tool": "", "tool_labels": {}, "tool_input": "", "message_files": []}
data: {"event": "message_end", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "metadata": {"usage": {"prompt_tokens": 8, "prompt_unit_price": "0.15", "prompt_price_unit": "0.000001", "prompt_price": "0.0000012", "completion_tokens": 9, "completion_unit_price": "0.60", "completion_price_unit": "0.000001", "completion_price": "0.0000054", "total_tokens": 17, "total_price": "0.0000066", "currency": "USD", "latency": 0.5811177659779787}}}
data: {"event": "tts_message_end", "conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610", "message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3", "created_at": 1726822243, "task_id": "397553c1-805d-45f7-b64e-4976b808c961", "audio": ""}
conversation_id:79d01e67-3b26-4d83-b138-8cce734d7610
响应结果
{
"limit": 20,
"has_more": false,
"data": [
{
"id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3",
"conversation_id": "79d01e67-3b26-4d83-b138-8cce734d7610",
"inputs": {},
"query": "hello",
"message": [
{
"role": "user",
"text": "hello",
"files": []
}
],
"message_tokens": 8,
"answer": "Hello! How can I assist you today?",
"answer_tokens": 9,
"provider_response_latency": 0.6008692521136254,
"from_source": "console",
"from_end_user_id": null,
"from_account_id": "b010bb9b-9098-4f07-a10e-39a6b6686d03",
"feedbacks": [],
"workflow_run_id": null,
"annotation": null,
"annotation_hit_history": null,
"created_at": 1726822243,
"agent_thoughts": [
{
"id": "7409b3c5-8952-498e-bd9d-e36a168a53e6",
"chain_id": null,
"message_id": "4692090d-09a4-4cdd-b768-2dc6d13e45f3",
"position": 1,
"thought": "Hello! How can I assist you today?",
"tool": "",
"tool_labels": {},
"tool_input": "",
"created_at": 1726822242,
"observation": "",
"files": []
}
],
"message_files": [],
"metadata": {},
"status": "normal",
"error": null
}
]
}
后端源码 console/app/completion
api.add_resource(ChatMessageApi, "/apps/<uuid:app_id>/chat-messages")
class ChatMessageApi(Resource):
@setup_required
@login_required
@account_initialization_required
@get_app_model(mode=[AppMode.CHAT, AppMode.AGENT_CHAT])
def post(self, app_model):
parser = reqparse.RequestParser()
parser.add_argument("inputs", type=dict, required=True, location="json")
parser.add_argument("query", type=str, required=True, location="json")
parser.add_argument("files", type=list, required=False, location="json")
parser.add_argument("model_config", type=dict, required=True, location="json")
parser.add_argument("conversation_id", type=uuid_value, location="json")
parser.add_argument("response_mode", type=str, choices=["blocking", "streaming"], location="json")
parser.add_argument("retriever_from", type=str, required=False, default="dev", location="json")
args = parser.parse_args()
streaming = args["response_mode"] != "blocking"
args["auto_generate_name"] = False
account = flask_login.current_user
try:
response = AppGenerateService.generate(
app_model=app_model, user=account, args=args, invoke_from=InvokeFrom.DEBUGGER, streaming=streaming
)
问题
- app_model是怎么来的
- tenant_id到底是什么?
AppGenerateService.generate
if app_model.mode == AppMode.COMPLETION.value:
return rate_limit.generate(
CompletionAppGenerator().generate(
app_model=app_model, user=user, args=args, invoke_from=invoke_from, stream=streaming
),
request_id,
)
elif app_model.mode == AppMode.AGENT_CHAT.value or app_model.is_agent:
return rate_limit.generate(
AgentChatAppGenerator().generate(
app_model=app_model, user=user, args=args, invoke_from=invoke_from, stream=streaming
),
request_id,
)
elif app_model.mode == AppMode.CHAT.value:
return rate_limit.generate(
ChatAppGenerator().generate(
app_model=app_model, user=user, args=args, invoke_from=invoke_from, stream=streaming
),
request_id,
)
ChatAppGenerator
def generate(
self, app_model: App,
user: Union[Account, EndUser],
args: Any,
invoke_from: InvokeFrom,
stream: bool = True,
) -> Union[dict, Generator[dict, None, None]]:
# new thread
worker_thread = threading.Thread(target=self._generate_worker, kwargs={
'flask_app': current_app._get_current_object(),
'application_generate_entity': application_generate_entity,
'queue_manager': queue_manager,
'conversation_id': conversation.id,
'message_id': message.id,
})
def _generate_worker(self, flask_app: Flask,
application_generate_entity: ChatAppGenerateEntity,
queue_manager: AppQueueManager,
conversation_id: str,
message_id: str) -> None:
runner = ChatAppRunner()
runner.run(
application_generate_entity=application_generate_entity,
queue_manager=queue_manager,
conversation=conversation,
message=message
)
ChatAppRunner无fc_agent_runner
AgentChatAppGenerator含有fc_agent_runner
# get conversation and message
conversation = self._get_conversation(conversation_id)
message = self._get_message(message_id)
# chatbot app
runner = AgentChatAppRunner()
runner.run(
application_generate_entity=application_generate_entity,
queue_manager=queue_manager,
conversation=conversation,
message=message,
)
AgentChatAppRunner
elif agent_entity.strategy == AgentEntity.Strategy.FUNCTION_CALLING:
runner_cls = FunctionCallAgentRunner
runner = runner_cls(
tenant_id=app_config.tenant_id,
application_generate_entity=application_generate_entity,
conversation=conversation,
app_config=app_config,
model_config=application_generate_entity.model_conf,
config=agent_entity,
queue_manager=queue_manager,
message=message,
user_id=application_generate_entity.user_id,
memory=memory,
prompt_messages=prompt_message,
variables_pool=tool_variables,
db_variables=tool_conversation_variables,
model_instance=model_instance
)
invoke_result = runner.run(
message=message,
query=query,
inputs=inputs,
)
FunctionCallAgentRunner
# save thought
self.save_agent_thought(
agent_thought=agent_thought,
tool_name=tool_call_names,
tool_input=tool_call_inputs,
thought=response,
tool_invoke_meta=None,
observation=None,
answer=response,
messages_ids=[],
llm_usage=current_llm_usage
)
self.queue_manager.publish(QueueAgentThoughtEvent(
agent_thought_id=agent_thought.id
), PublishFrom.APPLICATION_MANAGER)
final_answer += response + '\n'
# call tools
tool_responses = []
for tool_call_id, tool_call_name, tool_call_args in tool_calls:
tool_instance = tool_instances.get(tool_call_name)
if not tool_instance:
tool_response = {
"tool_call_id": tool_call_id,
"tool_call_name": tool_call_name,
"tool_response": f"there is not a tool named {tool_call_name}",
"meta": ToolInvokeMeta.error_instance(f"there is not a tool named {tool_call_name}").to_dict()
}
else:
# invoke tool
tool_invoke_response, message_files, tool_invoke_meta = ToolEngine.agent_invoke(
tool=tool_instance,
tool_parameters=tool_call_args,
user_id=self.user_id,
tenant_id=self.tenant_id,
message=self.message,
invoke_from=self.application_generate_entity.invoke_from,
agent_tool_callback=self.agent_callback,
trace_manager=trace_manager,
)
toolEngine
def agent_invoke(
tool: Tool, tool_parameters: Union[str, dict],
user_id: str, tenant_id: str, message: Message, invoke_from: InvokeFrom,
agent_tool_callback: DifyAgentCallbackHandler,
trace_manager: Optional[TraceQueueManager] = None
) -> tuple[str, list[tuple[MessageFile, bool]], ToolInvokeMeta]:
Buy me a cup of coffee ☕.
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· winform 绘制太阳,地球,月球 运作规律
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人