Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

update issues THUDM#618 使用tools时无法stream流式输出的问题 #619

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

jurnea
Copy link

@jurnea jurnea commented Oct 30, 2024

解决glm_server.py代码带有tools流式输出时,没有按照预期效果流式输出的问题,详见 issues #618
依赖包:

pip install langchain==0.2.16
pip install langgraph==0.2.34
pip install langchain_openai==0.1.9

使用如下代码:
记得更换URL 和 你的KEY(如果有)

import asyncio

from typing import Annotated
from typing_extensions import TypedDict
from langgraph.graph.message import add_messages
from langchain_core.tools import tool
from langgraph.prebuilt import ToolNode
from langchain_openai import ChatOpenAI
from langchain_core.runnables import RunnableConfig
from langgraph.graph import END, START, StateGraph
from langchain_core.messages import AIMessageChunk, HumanMessage, SystemMessage, AnyMessage

"""
This script build an agent by langgraph and stream LLM tokens
pip install langchain==0.2.16
pip install langgraph==0.2.34
pip install langchain_openai==0.1.9
"""


class State(TypedDict):
    messages: Annotated[list, add_messages]


@tool
def search(query: str):
    """Call to surf the web."""
    return ["Cloudy with a chance of hail."]


tools = [search]

model = ChatOpenAI(
    temperature=0,
    # model="glm-4",
    model="GLM-4-Flash",
    openai_api_key="[You Key]",
    # openai_api_base="https://open.bigmodel.cn/api/paas/v4/", #使用智谱官方提供的是正常流式输出
    openai_api_base="You url by glm_server.py ",
    streaming=True
)


class Agent:

    def __init__(self, model, tools, system=""):
        self.system = system
        workflow = StateGraph(State)
        workflow.add_node("agent", self.call_model)
        workflow.add_node("tools", ToolNode(tools))
        workflow.add_edge(START, "agent")
        workflow.add_conditional_edges(
            # First, we define the start node. We use `agent`.
            # This means these are the edges taken after the `agent` node is called.
            "agent",
            # Next, we pass in the function that will determine which node is called next.
            self.should_continue,
            # Next we pass in the path map - all the nodes this edge could go to
            ["tools", END],
        )
        workflow.add_edge("tools", "agent")
        self.model = model.bind_tools(tools)
        self.app = workflow.compile()

    def should_continue(self, state: State):
        messages = state["messages"]
        last_message = messages[-1]
        # If there is no function call, then we finish
        if not last_message.tool_calls:
            return END
        # Otherwise if there is, we continue
        else:
            return "tools"

    async def call_model(self, state: State, config: RunnableConfig):
        messages = state["messages"]
        if self.system:
            messages = [SystemMessage(content=self.system)] + messages
        response = await self.model.ainvoke(messages, config)
        # We return a list, because this will get added to the existing list
        return {"messages": response}

    async def query(self, user_input: str):
        inputs = [HumanMessage(content=user_input)]
        first = True
        async for msg, metadata in self.app.astream({"messages": inputs}, stream_mode="messages"):
            if msg.content and not isinstance(msg, HumanMessage):
                # 这里可以看出是否正常流式输出
                print(msg.content, end="|", flush=True)

            if isinstance(msg, AIMessageChunk):
                if first:
                    gathered = msg
                    first = False
                else:
                    gathered = gathered + msg

                if msg.tool_call_chunks:
                    print('tool_call_chunks...', gathered.tool_calls)


if __name__ == '__main__':

    input = "what is the weather in sf"
    prompt = """
    You are smart research assistant. Use the search engine ...
    """
    agent = Agent(model, tools, prompt)
    asyncio.run(agent.query(input))

这个代码示例链接GitHub分支

@zhipuch
Copy link
Collaborator

zhipuch commented Jan 14, 2025

尝试使用你的代码测试没有得到预期的结果

@jurnea
Copy link
Author

jurnea commented Jan 14, 2025

一:我提的问题能否重现??

  • 使用我的上面的测试代码 调用以你们目前/basic_demo/glm_server.py 作为服务端 是否能重现问题?? 非流式输出的

二: 替换成我提交的/basic_demo/glm_server.py 作为服务端后,能否解决问题?(流式输出的),是否报错?

TIPS:
因为我本地没有重现这个有问题的环境测试,公司内网生产代码glm_server.py无法拿出来,所以是手敲出来的,不知是否有问题。

流式输出效果:
I|'m| sorry|,| but| I| can|'t| provide| real|-time| weather| information|.| However|,| you| can| easily| check| the| current| weather| in| San| Francisco| by| using| a| search engine| or| a| weather| app|.|

非流式输出效果:没有 | 分割,一次性输出。

@zhipuch
Copy link
Collaborator

zhipuch commented Jan 14, 2025

一:我提的问题能否重现??

  • 使用我的上面的测试代码 调用以你们目前/basic_demo/glm_server.py 作为服务端 是否能重现问题?? 非流式输出的

二: 替换成我提交的/basic_demo/glm_server.py 作为服务端后,能否解决问题?(流式输出的),是否报错?

TIPS: 因为我本地没有重现这个有问题的环境测试,公司内网生产代码glm_server.py无法拿出来,所以是手敲出来的,不知是否有问题。

流式输出效果: I|'m| sorry|,| but| I| can|'t| provide| real|-time| weather| information|.| However|,| you| can| easily| check| the| current| weather| in| San| Francisco| by| using| a| search engine| or| a| weather| app|.|

非流式输出效果:没有 | 分割,一次性输出。

  1. 输出是非流式的,但是输出内容没问题
  2. 代码也有点问题,len那里应该少了key,调通之后输出空

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants