/images/hugo/avatar.png

mcp-go

这一节我们来学习 mcp go 语言实现的框架 mcp-go

1. 说明

为了快速对 mcp 有个整体的了解,我结合 ABcoder 向 mcp 提了几个问题,后面的内容基于这些问题回答的整理。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
提问:
# 问题一
请你给我介绍一下 mcp-go 中的核心对象

# 问题二
mcp-go 底层是 http server 么,借助什么框架实现的

# 问题三
我现在不清楚
- AddTool/AddTools : 添加工具
- AddResource/AddResources : 添加资源
- AddPrompt/AddPrompts : 添加提示
- AddResourceTemplate/AddResourceTemplates : 添加资源模板
这些方法执行之后,是如何形成 http 接口的。能不能举例说明一下,执行这些函数之后,mcp server 会提供哪些接口,与这些方法之间的对应关系

# 问题四
我想知道  mcp-go 接收到  tool_call 后

1. 生成 json rpc call 的位置
2. 路由找到对应的 tool handle 的位置

1. 核心包结构

根据对代码库的分析,MCP-Go 是一个实现了 Model Context Protocol (MCP) 的 Go 语言库,它提供了客户端和服务端之间进行通信的框架。以下是 MCP-Go 中的核心对象:

langgraph 重要流程总结

现在我们学习完了 Langgraph 的所有内容,本节我们要总结回顾一下 Langgraph 的重要流程,包括

  1. 中断和恢复
  2. 节点间的参数传递
  3. channel finish 的执行逻辑

1. 中断和恢复

LangGraph 中,通过 Command, interrupt、checkpointer 可以实现允许根据用户反馈暂停和恢复执行。

langgraph api 流程总结

本节我们总结回顾一下 Langgraph API 相关重要的流程,包括

  1. StateGraph API 如何映射为 Pregel
  2. Function API 如何映射为 Pregel

1. StateGraph API 如何映射为 Pregel

在之前的总结里,我们已经学习了 Pregel 执行的过程:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
                      input
                        |
                        | 初始化
                        |
                  updated_channels
                              ^
                   /          \ \
             更新 /        触发 \ \ 生成
                 /              \ \
            channel --- 数据 ---> node

在 Pregel 的抽象里只包含 channel、node(PregelNode) 两个核心组件。

langgraph graph api

这一节我们来学习 langgraph 的 graph api。

1. graph api

1.1 graph api 使用示例

我们来看 langgraph 官方文档提供的智能机器人的示例:

 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
from typing import Annotated
from dotenv import load_dotenv, find_dotenv
from langchain_community.chat_models import ChatTongyi

_ = load_dotenv(find_dotenv())  # read local .env file

llm = ChatTongyi(temperature=0.0)

from typing import Annotated

from langchain_tavily import TavilySearch
from langchain_core.messages import BaseMessage
from typing_extensions import TypedDict

from langgraph.graph import StateGraph, START, END
from langgraph.graph.message import add_messages
from langgraph.prebuilt import ToolNode, tools_condition


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


graph_builder = StateGraph(State)

tool = TavilySearch(max_results=2)
tools = [tool]
llm_with_tools = llm.bind_tools(tools)


def chatbot(state: State):
    return {"messages": [llm_with_tools.invoke(state["messages"])]}


graph_builder.add_node("chatbot", chatbot)

tool_node = ToolNode(tools=[tool])
graph_builder.add_node("tools", tool_node)

graph_builder.add_conditional_edges(
    "chatbot",
    tools_condition,
)
# Any time a tool is called, we return to the chatbot to decide the next step
graph_builder.add_edge("tools", "chatbot")
graph_builder.add_edge(START, "chatbot")
graph = graph_builder.compile()

print(graph.get_graph().draw_ascii())

1.2 graph api 类图

/images/langgraph/stategraph.svg

pregel prebuit

langgraph 在 prebuilt 下提供了一个 chat agent 实现。这个 agent 比较复杂,为了讲清楚,我们分成几个部分来讲解。

  1. 参数校验

1. 参数校验

state_schema:

  • 必须包含 messagesremaining_steps 两个字段。
  • 如果存在 pre_model_hook 需要给 state 添加 llm_input_messages 收集 pre_model_hook 输出的 message
 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
def create_react_agent(
    model: Union[
        str,
        LanguageModelLike,
        Callable[[StateSchema, Runtime[ContextT]], BaseChatModel],
        Callable[[StateSchema, Runtime[ContextT]], Awaitable[BaseChatModel]],
    ],
    tools: Union[Sequence[Union[BaseTool, Callable, dict[str, Any]]], ToolNode],
    *,
    prompt: Optional[Prompt] = None,
    response_format: Optional[
        Union[StructuredResponseSchema, tuple[str, StructuredResponseSchema]]
    ] = None,
    pre_model_hook: Optional[RunnableLike] = None,
    post_model_hook: Optional[RunnableLike] = None,
    state_schema: Optional[StateSchemaType] = None,
    context_schema: Optional[Type[Any]] = None,
    checkpointer: Optional[Checkpointer] = None,
    store: Optional[BaseStore] = None,
    interrupt_before: Optional[list[str]] = None,
    interrupt_after: Optional[list[str]] = None,
    debug: bool = False,
    version: Literal["v1", "v2"] = "v2",
    name: Optional[str] = None,
    **deprecated_kwargs: Any,
) -> CompiledStateGraph:
    if (
        config_schema := deprecated_kwargs.pop("config_schema", MISSING)
    ) is not MISSING:
        warn(
            "`config_schema` is deprecated and will be removed. Please use `context_schema` instead.",
            category=LangGraphDeprecatedSinceV10,
        )

        if context_schema is None:
            context_schema = config_schema

    if version not in ("v1", "v2"):
        raise ValueError(
            f"Invalid version {version}. Supported versions are 'v1' and 'v2'."
        )

    if state_schema is not None:
        required_keys = {"messages", "remaining_steps"}
        if response_format is not None:
            required_keys.add("structured_response")

        schema_keys = set(get_type_hints(state_schema))
        if missing_keys := required_keys - set(schema_keys):
            raise ValueError(f"Missing required key(s) {missing_keys} in state_schema")

    if state_schema is None:
        state_schema = (
            AgentStateWithStructuredResponse
            if response_format is not None
            else AgentState
        )

    input_schema: StateSchemaType
    if pre_model_hook is not None:
        # Dynamically create a schema that inherits from state_schema and adds 'llm_input_messages'
        if isinstance(state_schema, type) and issubclass(state_schema, BaseModel):
            # For Pydantic schemas
            from pydantic import create_model

            input_schema = create_model(
                "CallModelInputSchema",
                llm_input_messages=(list[AnyMessage], ...),
                __base__=state_schema,
            )
        else:
            # For TypedDict schemas
            class CallModelInputSchema(state_schema):  # type: ignore
                llm_input_messages: list[AnyMessage]

            input_schema = CallModelInputSchema
    else:
        input_schema = state_schema

2. tool 处理

tool 分为两类: