Browse Source

Feat: Add thought info to every component. (#9134)

### What problem does this PR solve?

#9082 #6365

### Type of change

- [x] New Feature (non-breaking change which adds functionality)
tags/v0.20.0
Kevin Hu 3 months ago
parent
commit
3f6177b5e5
No account linked to committer's email address

+ 0
- 45
agent/README.md View File

@@ -1,45 +0,0 @@
English | [简体中文](./README_zh.md)

# *Graph*


## Introduction

*Graph* is a mathematical concept which is composed of nodes and edges.
It is used to compose a complex work flow or agent.
And this graph is beyond the DAG that we can use circles to describe our agent or work flow.
Under this folder, we propose a test tool ./test/client.py which can test the DSLs such as json files in folder ./test/dsl_examples.
Please use this client at the same folder you start RAGFlow. If it's run by Docker, please go into the container before running the client.
Otherwise, correct configurations in service_conf.yaml is essential.

```bash
PYTHONPATH=path/to/ragflow python graph/test/client.py -h
usage: client.py [-h] -s DSL -t TENANT_ID -m

options:
-h, --help show this help message and exit
-s DSL, --dsl DSL input dsl
-t TENANT_ID, --tenant_id TENANT_ID
Tenant ID
-m, --stream Stream output
```
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/79179c5e-d4d6-464a-b6c4-5721cb329899" width="1000"/>
</div>


## How to gain a TENANT_ID in command line?
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/419d8588-87b1-4ab8-ac49-2d1f047a4b97" width="600"/>
</div>
💡 We plan to display it here in the near future.
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/c97915de-0091-46a5-afd9-e278946e5fe3" width="600"/>
</div>


## How to set 'kb_ids' for component 'Retrieval' in DSL?
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/0a731534-cac8-49fd-8a92-ca247eeef66d" width="600"/>
</div>


+ 0
- 46
agent/README_zh.md View File

@@ -1,46 +0,0 @@
[English](./README.md) | 简体中文

# *Graph*


## 简介

"Graph"是一个由节点和边组成的数学概念。
它被用来构建复杂的工作流或代理。
这个图超越了有向无环图(DAG),我们可以使用循环来描述我们的代理或工作流。
在这个文件夹下,我们提出了一个测试工具 ./test/client.py,
它可以测试像文件夹./test/dsl_examples下一样的DSL文件。
请在启动 RAGFlow 的同一文件夹中使用此客户端。如果它是通过 Docker 运行的,请在运行客户端之前进入容器。
否则,正确配置 service_conf.yaml 文件是必不可少的。

```bash
PYTHONPATH=path/to/ragflow python graph/test/client.py -h
usage: client.py [-h] -s DSL -t TENANT_ID -m

options:
-h, --help show this help message and exit
-s DSL, --dsl DSL input dsl
-t TENANT_ID, --tenant_id TENANT_ID
Tenant ID
-m, --stream Stream output
```
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/05924730-c427-495b-8ee4-90b8b2250681" width="1000"/>
</div>


## 命令行中的TENANT_ID如何获得?
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/419d8588-87b1-4ab8-ac49-2d1f047a4b97" width="600"/>
</div>
💡 后面会展示在这里:
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/c97915de-0091-46a5-afd9-e278946e5fe3" width="600"/>
</div>


## DSL里面的Retrieval组件的kb_ids怎么填?
<div align="center" style="margin-top:20px;margin-bottom:20px;">
<img src="https://github.com/infiniflow/ragflow/assets/12318111/0a731534-cac8-49fd-8a92-ca247eeef66d" width="600"/>
</div>


+ 4
- 0
agent/canvas.py View File

@@ -272,6 +272,7 @@ class Canvas:
"component_id": self.path[i],
"component_name": self.get_component_name(self.path[i]),
"component_type": self.get_component_type(self.path[i]),
"thoughts": self.get_component_thoughts(self.path[i])
})
_run_batch(idx, to)

@@ -523,3 +524,6 @@ class Canvas:
def get_memory(self) -> list[Tuple]:
return self.memory

def get_component_thoughts(self, cpn_id) -> str:
return self.components.get(cpn_id)["obj"].thoughts()


+ 1
- 1
agent/component/__init__.py View File

@@ -48,10 +48,10 @@ __all__ = list(__all_classes.keys()) + ["__all_classes"]

del _package_path, _import_submodules, _extract_classes_from_module


def component_class(class_name):
m = importlib.import_module("agent.component")
try:
return getattr(m, class_name)
except Exception:
return getattr(importlib.import_module("agent.tools"), class_name)


+ 2
- 1
agent/component/agent_with_tools.py View File

@@ -23,7 +23,6 @@ from typing import Any

import json_repair

from agent.component.llm import LLMParam, LLM
from agent.tools.base import LLMToolPluginCallSession, ToolParamBase, ToolBase, ToolMeta
from api.db.services.llm_service import LLMBundle, TenantLLMService
from api.db.services.mcp_server_service import MCPServerService
@@ -32,6 +31,7 @@ from rag.prompts import message_fit_in
from rag.prompts.prompts import next_step, COMPLETE_TASK, analyze_task, \
citation_prompt, reflect, rank_memories, kb_prompt, citation_plus, full_question
from rag.utils.mcp_tool_call_conn import MCPToolCallSession, mcp_tool_metadata_to_openai_tool
from agent.component.llm import LLMParam, LLM


class AgentParam(LLMParam, ToolParamBase):
@@ -330,3 +330,4 @@ Respond immediately with your final comprehensive answer.
logging.exception(e)

return "Error occurred."


+ 5
- 1
agent/component/base.py View File

@@ -13,9 +13,10 @@
# See the License for the specific language governing permissions and
# limitations under the License.
#

import re
import time
from abc import ABC
from abc import ABC, abstractmethod
import builtins
import json
import os
@@ -535,3 +536,6 @@ class ComponentBase(ABC):
def get_exception_default_value(self):
return self._param.exception_default_value

@abstractmethod
def thoughts(self) -> str:
...

+ 3
- 0
agent/component/begin.py View File

@@ -44,3 +44,6 @@ class Begin(UserFillUp):
v = v.get("value")
self.set_output(k, v)
self.set_input_value(k, v)

def thoughts(self) -> str:
return "☕ Here we go..."

+ 3
- 1
agent/component/categorize.py View File

@@ -20,7 +20,7 @@ from abc import ABC

from api.db import LLMType
from api.db.services.llm_service import LLMBundle
from agent.component import LLMParam, LLM
from agent.component.llm import LLMParam, LLM
from api.utils.api_utils import timeout
from rag.llm.chat_model import ERROR_PREFIX

@@ -133,3 +133,5 @@ class Categorize(LLM, ABC):
self.set_output("category_name", max_category)
self.set_output("_next", cpn_ids)

def thoughts(self) -> str:
return "Which should it falls into {}? ...".format(",".join([f"`{c}`" for c, _ in self._param.category_description.items()]))

+ 2
- 1
agent/component/fillup.py View File

@@ -34,6 +34,7 @@ class UserFillUp(ComponentBase):
for k, v in kwargs.get("inputs", {}).items():
self.set_output(k, v)


def thoughts(self) -> str:
return "Waiting for your input..."



+ 3
- 0
agent/component/invoke.py View File

@@ -137,3 +137,6 @@ class Invoke(ComponentBase, ABC):
return f"Http request error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Waiting for the server respond..."

+ 2
- 0
agent/component/iteration.py View File

@@ -53,6 +53,8 @@ class Iteration(ComponentBase, ABC):
if not isinstance(arr, list):
self.set_output("_ERROR", self._param.items_ref + " must be an array, but its type is "+str(type(arr)))

def thoughts(self) -> str:
return "Need to process {} items.".format(len(self._canvas.get_variable_value(self._param.items_ref)))




+ 2
- 0
agent/component/iterationitem.py View File

@@ -79,3 +79,5 @@ class IterationItem(ComponentBase, ABC):
def end(self):
return self._idx == -1

def thoughts(self) -> str:
return "Next turn..."

+ 4
- 0
agent/component/llm.py View File

@@ -240,3 +240,7 @@ class LLM(ComponentBase):
summ = tool_call_summary(self.chat_mdl, func_name, params, results)
logging.info(f"[MEMORY]: {summ}")
self._canvas.add_memory(user, assist, summ)

def thoughts(self) -> str:
_, msg = self._prepare_prompt_variables()
return f"I’m thinking and planning the next move, starting from the prompt:<br/>“{msg[-1]['content']}”<span class=\"collapse\"> (tap to see full text)</span>"

+ 2
- 0
agent/component/message.py View File

@@ -142,3 +142,5 @@ class Message(ComponentBase):

self.set_output("content", content)

def thoughts(self) -> str:
return "Thinking ..."

+ 2
- 0
agent/component/string_transform.py View File

@@ -94,5 +94,7 @@ class StringTransform(Message, ABC):

self.set_output("result", script)

def thoughts(self) -> str:
return f"It's {self._param.method}ing."



+ 4
- 1
agent/component/switch.py View File

@@ -125,4 +125,7 @@ class Switch(ComponentBase, ABC):
except Exception:
return True if input <= value else False

raise ValueError('Not supported operator' + operator)
raise ValueError('Not supported operator' + operator)

def thoughts(self) -> str:
return "I’m weighing a few options and will pick the next step shortly."

+ 15
- 0
agent/tools/__init__.py View File

@@ -1,3 +1,18 @@
#
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import importlib
import inspect

+ 6
- 0
agent/tools/arxiv.py View File

@@ -94,3 +94,9 @@ class ArXiv(ToolBase, ABC):
return f"ArXiv error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return """
Keywords: {}
Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!"))

+ 3
- 0
agent/tools/base.py View File

@@ -165,3 +165,6 @@ class ToolBase(ComponentBase):
})
self._canvas.add_refernce(chunks, aggs)
self.set_output("formalized_content", "\n".join(kb_prompt({"chunks": chunks, "doc_aggs": aggs}, 200000, True)))

def thoughts(self) -> str:
return self._canvas.get_component_name(self._id) + " is running..."

+ 2
- 1
agent/tools/code_exec.py View File

@@ -189,4 +189,5 @@ class CodeExec(ToolBase, ABC):
def _encode_code(self, code: str) -> str:
return base64.b64encode(code.encode("utf-8")).decode("utf-8")


def thoughts(self) -> str:
return "Running a short script to process data."

+ 6
- 0
agent/tools/duckduckgo.py View File

@@ -112,3 +112,9 @@ class DuckDuckGo(ToolBase, ABC):
return f"DuckDuckGo error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return """
Keywords: {}
Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!"))

+ 9
- 1
agent/tools/email.py View File

@@ -204,4 +204,12 @@ class Email(ToolBase, ABC):
self.set_output("_ERROR", str(last_e))
return False

assert False, self.output()
assert False, self.output()

def thoughts(self) -> str:
inputs = self.get_input()
return """
To: {}
Subject: {}
Your email is on its way—sit tight!
""".format(inputs.get("to_email", "-_-!"), inputs.get("subject", "-_-!"))

+ 2
- 3
agent/tools/exesql.py View File

@@ -128,6 +128,5 @@ class ExeSQL(ToolBase, ABC):
self.set_output("formalized_content", "\n\n".join(formalized_content))
return self.output("formalized_content")


def debug(self, **kwargs):
return self._run([], **kwargs)
def thoughts(self) -> str:
return "Query sent—waiting for the data."

+ 3
- 0
agent/tools/github.py View File

@@ -86,3 +86,6 @@ class GitHub(ToolBase, ABC):
return f"GitHub error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Scanning GitHub repos related to `{}`.".format(self.get_input().get("query", "-_-!"))

+ 5
- 0
agent/tools/google.py View File

@@ -152,3 +152,8 @@ class Google(ToolBase, ABC):

assert False, self.output()

def thoughts(self) -> str:
return """
Keywords: {}
Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!"))

+ 3
- 0
agent/tools/googlescholar.py View File

@@ -91,3 +91,6 @@ class GoogleScholar(ToolBase, ABC):
return f"GoogleScholar error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!"))

+ 3
- 0
agent/tools/pubmed.py View File

@@ -103,3 +103,6 @@ class PubMed(ToolBase, ABC):
return f"PubMed error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Looking for scholarly papers on `{}`,” prioritising reputable sources.".format(self.get_input().get("query", "-_-!"))

+ 6
- 0
agent/tools/retrieval.py View File

@@ -159,3 +159,9 @@ class Retrieval(ToolBase, ABC):
form_cnt = "\n".join(kb_prompt(kbinfos, 200000, True))
self.set_output("formalized_content", form_cnt)
return form_cnt

def thoughts(self) -> str:
return """
Keywords: {}
Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!"))

+ 9
- 0
agent/tools/tavily.py View File

@@ -134,6 +134,12 @@ class TavilySearch(ToolBase, ABC):

assert False, self.output()

def thoughts(self) -> str:
return """
Keywords: {}
Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!"))


class TavilyExtractParam(ToolParamBase):
"""
@@ -216,3 +222,6 @@ class TavilyExtract(ToolBase, ABC):
return f"Tavily error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Opened {}—pulling out the main text…".format(self.get_input().get("urls", "-_-!"))

+ 3
- 0
agent/tools/wencai.py View File

@@ -109,3 +109,6 @@ class WenCai(ToolBase, ABC):
return f"WenCai error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Pulling live financial data for `{}`.".format(self.get_input().get("query", "-_-!"))

+ 6
- 0
agent/tools/wikipedia.py View File

@@ -96,3 +96,9 @@ class Wikipedia(ToolBase, ABC):
return f"Wikipedia error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return """
Keywords: {}
Looking for the most relevant articles.
""".format(self.get_input().get("query", "-_-!"))

+ 3
- 0
agent/tools/yahoofinance.py View File

@@ -109,3 +109,6 @@ class YahooFinance(ToolBase, ABC):
return f"YahooFinance error: {last_e}"

assert False, self.output()

def thoughts(self) -> str:
return "Pulling live financial data for `{}`.".format(self.get_input().get("stock_code", "-_-!"))

Loading…
Cancel
Save