AI应用开发——常用 框架/设计模式 的代码示例
主要介绍使用这些框架,代码和范式会是什么样的,有助于我们快速理解他人项目/ai写的代码。
1. functioncalling应用
预先定义一组函数(名称、描述、参数)。
在请求中传入这些函数,让模型决定是否调用。
模型返回时附带
tool_calls信息,包含函数名和参数。代码解析并执行对应的本地函数,将结果返回给模型。
模型利用结果生成最终回复。
---
1. 定义工具(tools)
tools = [
{
"type": "function",
"function": {
"name": "solve",
"description": "求解方程",
"parameters": { ... }
}
},
{
"type": "function",
"function": {
"name": "multiply",
"description": "计算大数字乘积",
"parameters": { ... }
}
}
]
```
- 使用 OpenAI 风格的 `tools` 参数,明确声明了两个可被模型调用的函数:`solve` 和 `multiply`。
- 每个函数都提供了名称、描述和参数结构(JSON Schema),这是 Function Calling 的前提。
---
2. 在请求中启用 Function Calling
response = client.chat.completions.create(
model=model_name,
messages=messages,
tools=tools, # 传入工具列表
tool_choice="auto", # 允许模型自动选择是否调用工具
temperature=0.9
)
```
- 明确传递了 `tools` 参数,并设置 `tool_choice="auto"`,让模型自主决定是否调用工具以及调用哪个工具。这是触发 Function Calling 的关键步骤。
---
3. 处理模型返回的工具调用(tool_calls)
if model_response.choices[0].message.tool_calls:
tool_call = model_response.choices[0].message.tool_calls[0]
args = tool_call.function.arguments
if tool_call.function.name == "solve":
function_result = solve(**json.loads(args))
elif tool_call.function.name == "multiply":
function_result = multiply(**json.loads(args))
...
```
- 模型如果决定调用工具,会在返回的 `message` 中包含 `tool_calls` 字段。
- 代码检查该字段,解析出函数名和参数(`arguments` 是 JSON 字符串),然后执行对应的本地 Python 函数(`solve` 或 `multiply`),并将结果存储在 `function_result` 中。
---
4. 将工具执行结果反馈给模型
messages.append({
"role": "tool",
"content": f"{json.dumps(function_result)}",
"tool_call_id": tool_call.id
})
response = client.chat.completions.create(
model=model_name,
messages=messages,
tools=tools,
temperature=0.9
)
```
- 将工具的执行结果以 `role: "tool"` 的形式追加到对话历史中,并关联对应的 `tool_call_id`(用于匹配原始调用)。
- 再次调用模型,让模型基于工具返回的结果生成最终的自然语言回答。
---
5. 工具函数的实际定义
def solve(symbols: str, equation: str):
... # 使用 sympy 求解方程
def multiply(multiplicand: float, multiplier: float):
... # 计算乘积
```
- 这是 Function Calling 的具体实现,即当模型决定调用工具时,实际执行的代码逻辑。
---
2.AgentScope框架-react 设计模式(边思考边执行)
ReAct 的优势就在于它能根据实际情况灵活应对,而不是遇到意外就卡住。整个过程是观察-思考-行动-再观察的循环,而不是一开始就把所有步骤都定死。每一步都会根据当前的历史上下文和环境信息,动态决定下一步该做什么。
对于需要一定自主性但又不需要复杂规划的场景,可以从子节点入手,使用ReAct模式提升Agent的决策能力;大部分业务场景可用 固定流程 + 局部ReAct的混合架构;
---
1. 导入 `agentscope` 及其子模块
import agentscope
from agentscope.agents import UserAgent
```
直接导入 `agentscope` 库,以及其内置的 `UserAgent`、`ReActAgent` 和工具服务相关类(`ServiceToolkit`、`ServiceResponse`、`ServiceExecStatus`)。
---
2. 使用 AgentScope 的模型配置格式
MODEL_CONFIG = {
"config_name": MODEL_CONFIG_NAME,
"model_type": "openai_chat",
"model_name": "glm-4-9b-chat",
"api_key": "EMPTY",
"organization": "EMPTY"
}
```
这是一个典型的 AgentScope 模型配置字典,符合框架要求的格式(`config_name`、`model_type`、`model_name` 等),并通过 `agentscope.init()` 加载。
---
3. 调用 `agentscope.init()` 初始化框架
agentscope.init(
model_configs=MODEL_CONFIG,
project="ReActAgent",
)
```
AgentScope 必须通过 `init()` 完成全局设置(如加载模型配置、设置项目名称)。
---
4. 创建 AgentScope 内置的 Agent 类型
- `ReActAgent`:AgentScope 内置的 ReAct 模式智能体,能够根据观察(Observation)和思考(Thought)循环调用工具。
- `UserAgent`:AgentScope 提供的用户代理,用于模拟用户输入。
reActAgent = ReActAgent(
name="assistant",
model_config_name=MODEL_CONFIG_NAME,
verbose=True,
service_toolkit=init_toolkit(),
)
userAgent = UserAgent(name="User")
```
它们都继承自 AgentScope 的基类 `AgentBase`,并遵循框架的对话管理机制。
---
5. 使用 `ServiceToolkit` 封装工具函数
service_toolkit = ServiceToolkit()
service_toolkit.add(execute_python_code)
```
AgentScope 提供 `ServiceToolkit` 来管理可被 Agent 调用的外部工具。通过 `add()` 方法将自定义函数注册为服务,函数返回值必须是 `ServiceResponse` 类型(包含状态和内容)。
```
def execute_python_code(code: str) -> ServiceResponse:
...
return ServiceResponse(status, output)
```
函数签名和返回值严格遵循 AgentScope 的服务规范。
---
6. Agent 的调用方式符合 AgentScope 交互模式
user_response = userAgent(None) # 生成用户消息
reActAgent(user_response) # 将用户消息传递给 ReActAgent 处理
```
AgentScope 的 Agent 对象是可调用的,输入消息(通常为 `Msg` 对象或字符串),返回响应。这里用 `userAgent(None)` 触发用户输入(可能在控制台等待输入),然后交给 `reActAgent` 处理,体现了 AgentScope 的对话流。
---
7. 异常处理中隐含的 Agent 运行机制
for _ in range(3):
try:
reActAgent(user_response)
break
except:
pass
```
AgentScope 的 Agent 在执行过程中可能抛出异常(如工具调用失败),这里用重试机制简单处理,但整体仍遵循 Agent 的单轮对话调用方式。
3. langchain框架-plan-execute设计模式(先计划后执行)
如果业务流程本身就是确定的(比如订单处理、审批流程),使用固定的Agent编排会更加高效可控;
1. 导入 Plan-and-Execute 相关模块
from langchain_experimental.plan_and_execute \
import PlanAndExecute, load_agent_executor, load_chat_planner
```
- `PlanAndExecute`:组合规划器和执行器的顶级代理类。
- `load_chat_planner`:创建一个基于聊天模型的规划器,负责将复杂任务分解为有序步骤。
- `load_agent_executor`:创建一个执行器(通常是一个 ReAct 风格的代理),负责实际执行每一步。
---
2. 工具定义
class BingSearchTool(BaseTool):
name = "搜索"
description = "当您需要回答有关当前事件或世界当前状态的问题时很有用"
def _run(self, query):
data = search_with_bing(query["title"])
return data
def init_tools():
llm = OpenAI(openai_params)
llm_math_chain = LLMMathChain.from_llm(llm=llm, verbose=True)
tools = [
BingSearchTool(),
Tool(
name="计算器",
func=llm_math_chain.run,
description="在需要回答数学问题时很有用",
handle_tool_error=_handle_tool_error
),
]
return tools
```
- 定义了两种工具:Bing 搜索和数学计算器。
- 这些工具将提供给执行器使用,以便在执行每个步骤时调用。
---
3. 创建规划器和执行器
def init_agent():
model = ChatOpenAI(**openai_params)
planner = load_chat_planner(model) # 创建规划器
tools = init_tools()
executor = load_agent_executor(model, tools, verbose=True) # 创建执行器
agent = PlanAndExecute(planner=planner, executor=executor, verbose=True)
return agent
```
- 规划器(planner):`load_chat_planner(model)` 返回一个规划器,它接受用户请求,并输出一个步骤列表(`Plan` 对象)。规划器通常基于提示模板,引导模型思考如何将任务分解。
- 执行器(executor):`load_agent_executor(model, tools, verbose=True)` 返回一个执行器,它实际上是一个标准的 LangChain 代理(如 ReAct),能够按顺序执行每个步骤,并在需要时调用工具。
- PlanAndExecute 代理:将规划器和执行器组合成一个完整的代理,内部自动处理“先规划后执行”的流程。
---
4. 使用 PlanAndExecute 代理
response = agent.invoke(prompt)
```
- `agent.invoke(prompt)` 会触发完整的 Plan-and-Execute 流程:
1. 规划器将 `prompt` 分解为多个步骤。
2. 执行器依次执行每个步骤,可能调用工具(如搜索、计算器)。
3. 最终返回一个包含最终答案的响应(`response["output"]`)。
---
5. 提示词构建
prompt_template = PromptTemplate.from_template(
"搜索一下以下问题:{query},并进行分析," +
"如果问题中含有数学计算,请用计算器进行计算," +
"最后用中文给出答案"
)
prompt = prompt_template.format(query=query)
```
- 这里的提示词是一个面向用户的指令,不是直接给模型的任务分解。规划器会基于这个指令生成步骤清单。
- Plan-and-Execute 的规划器是自主工作的,它不需要在提示中明确要求分解步骤,而是由内置的规划逻辑自动完成。
4.LangGraph
LangGraph 让开发者能够以图的形式精确控制智能体的行为,特别适合需要循环、分支、工具调用和多轮交互的复杂场景。
1 显式导入 LangGraph 模块
from langgraph.prebuilt import ToolNode
from langgraph.graph import END, StateGraph, MessagesState
from langgraph.checkpoint import MemorySaver
```
- `ToolNode`:LangGraph 预构建的节点,用于批量执行工具调用。
- `StateGraph`:LangGraph 的核心类,用于构建状态机式的多步工作流。
- `END`:特殊节点,表示流程结束。
- `MessagesState`:预定义的状态类型,包含消息列表,便于跟踪对话。
- `MemorySaver`:检查点保存器,用于在节点间持久化状态(实现对话记忆)。
2 使用 StateGraph 构建工作流
workflow = StateGraph(MessagesState)
workflow.add_node("agent", call_model)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", should_continue)
workflow.add_edge("tools", 'agent')
```
- `StateGraph` 的创建和节点添加是 LangGraph 的标准模式。
- `add_conditional_edges` 添加条件路由,根据函数返回值决定下一个节点。
- `add_edge` 添加固定边。
- 这种图结构清晰地定义了智能体的循环逻辑,是 LangGraph 区别于普通 LangChain 链的关键。
3 条件判断函数 `should_continue`
def should_continue(state: MessagesState) -> Literal["tools", END]:
if last_message.tool_calls:
return "tools"
return END
```
- 函数接收当前状态,返回下一个节点的名称。这正是 LangGraph 条件边的典型用法。
4 使用 `MemorySaver` 保存状态
checkpointer = MemorySaver()
app = workflow.compile(checkpointer=checkpointer)
```
- `MemorySaver` 实现了检查点接口,允许在多次调用间保持状态(例如同一对话的不同轮次),这是 LangGraph 支持多轮对话的底层机制。
5 流式执行 `app.stream`
for output in app.stream(inputs, config={"configurable": {"thread_id": 42}}):
...
```
- `app.stream` 是 LangGraph 编译后应用的执行方法,按节点执行顺序逐步输出结果,支持调试和实时观察。
- 传入的 `thread_id` 用于区分不同会话,与检查点结合实现对话隔离。
6 与 LangChain 生态的融合
- 使用 `@tool` 装饰器定义工具,这是 LangChain 的标准工具定义方式。
- 使用 `ChatOpenAI` 并调用 `.bind_tools()`,这是 LangChain 将工具绑定到模型的方法。
- LangGraph 是 LangChain 社区提供的用于构建复杂智能体工作流的扩展库。
5.AutoGen
AutoGen 用于多智能体协作,一个主任务配合一个子任务,通过嵌套对话自动完成,非常适合需要角色分工、分步处理的复杂场景。
1 显式导入 `autogen`
import autogen
```
2 使用 AutoGen 的核心类
def init_agents():
programer = autogen.AssistantAgent(
name="programer",
llm_config={"config_list": config_list},
system_message="""
你是一个优秀的人工智能编程助手。
能够编写Python程序或编写JSON格式的文件
""",
)
reviewer = autogen.AssistantAgent(
name="reviewer",
llm_config={"config_list": config_list},
system_message="""
你是一个软件审核人员,能够阅读Python代码和JSON结构的文件,
你的任务是发现代码的问题和检查JSON的结构是否合规
""",
)
- `autogen.AssistantAgent`:创建两个助手角色 `programer` 和 `reviewer`,通过 `system_message` 赋予角色定义,通过 `llm_config` 连接模型。
- `autogen.UserProxyAgent`:创建用户代理,设置 `human_input_mode="NEVER"` 表示全自动运行,通过 `is_termination_msg` 定义结束条件。
3 注册嵌套对话
user_proxy.register_nested_chats(
[{"recipient": reviewer, "message": reflection_message,
"summary_method": "last_msg", "max_turns": 1}],
trigger=programer,
)
```
`register_nested_chats` 是 AutoGen 特有的方法,用于在特定触发条件(`trigger`)下启动一个子对话,这是 AutoGen 多智能体协作的核心机制。
4 发起对话的方式
response = user_proxy.initiate_chat(
recipient=programer, message=query, max_turns=2,
summary_method="last_msg"
)
```
`initiate_chat` 是 AutoGen 中启动对话的标准方法,返回包含 `chat_history` 的对象,便于后续处理。
5 使用 AutoGen 的消息历史
for _content in response.chat_history:
answer = answer + "***\n# To " + _content['role'] + ":\n"
answer = answer + _content['content'] + "\n"
```
`response.chat_history` 是 AutoGen 自动记录的对话历史,包含每条消息的角色和内容。
6 模型配置的 AutoGen 风格
config_list = [
{"model": "glm-4-9b-chat",
"base_url": "http://server-dev:8000/v1",
"api_key": "EMPTY",
"stream": False,
"cache_seed": None
}
]
```
`config_list` 是 AutoGen 中用于配置多个模型端点的标准格式,可支持模型切换、负载均衡等。
6.LlamaIndex
常用于构建检索增强生成(RAG)系统
1 显式导入 LlamaIndex 模块
from llama_index.core import (
SimpleDirectoryReader,
VectorStoreIndex,
StorageContext,
load_index_from_storage,
Settings
)
from llama_index.core.node_parser import SimpleNodeParser
from llama_index.core.tools import QueryEngineTool, ToolMetadata
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.llms.openai import OpenAI
from llama_index.agent.openai import OpenAIAgent
```
这些导入都是 LlamaIndex 的专有模块,特别是:
- `llama_index.core`:核心组件,包括索引、存储、读取器。
- `llama_index.embeddings.huggingface`:HuggingFace 嵌入模型封装。
- `llama_index.llms.openai`:OpenAI 风格 LLM 封装。
- `llama_index.agent.openai`:OpenAI 智能体实现。
2 使用 LlamaIndex 的核心类
- `SimpleDirectoryReader`:LlamaIndex 内置的文档加载器,可批量读取目录下的文件。
- `SimpleNodeParser`:将文档解析为节点(Node),是 LlamaIndex 处理文本的基本单位。
- `VectorStoreIndex`:向量索引的核心类,用于构建和查询向量存储。
- `StorageContext` 和 `load_index_from_storage`:管理索引的持久化和加载,是 LlamaIndex 存储机制的关键。
- `Settings`:LlamaIndex 的全局配置对象,用于设置默认的 LLM、嵌入模型等。
- `QueryEngineTool` 和 `ToolMetadata`:将查询引擎包装成工具,供 Agent 使用,这是 LlamaIndex 与智能体集成的标准方式。
- `OpenAIAgent`:LlamaIndex 实现的 OpenAI 风格 Agent,能够调用工具并管理对话。
3 配置全局设置
Settings.llm = llm
Settings.embed_model = HuggingFaceEmbedding(...)
```
`Settings` 是 LlamaIndex 特有的全局配置入口,用于统一设置模型,后续所有组件将自动使用这些配置。
4 工具封装方式
QueryEngineTool(
query_engine=engine,
metadata=ToolMetadata(
name="文档检索",
description=...
)
)
```
`QueryEngineTool` 是 LlamaIndex 专门为查询引擎设计的工具封装,与 LangChain 的 `Tool` 概念不同,但目的类似。
5 智能体创建
agent = OpenAIAgent.from_tools(query_engine_tool, verbose=True)
```
`OpenAIAgent.from_tools` 是 LlamaIndex 提供的工厂方法,用于创建一个可以调用工具的智能体,与 LangChain 的 Agent 有相似之处,但属于 LlamaIndex 生态。
6 索引构建流程
index = VectorStoreIndex(nodes)
index.storage_context.persist(persist_dir=index_path)
```
LlamaIndex 的索引自带 `storage_context`,可以直接持久化,无需额外配置。
7.CrewAI
常用于构建结构化的多智能体协作应用
1 显式导入 CrewAI 模块
from crewai import Agent, Task, Crew, Process
```
CrewAI 的核心组件:
- `Agent`:定义智能体的角色、目标、背景故事等。
- `Task`:定义任务描述、预期输出和分配给哪个智能体。
- `Crew`:组合多个智能体和任务,管理执行流程。
- `Process`:指定任务执行方式,如顺序执行(`sequential`)或分层(`hierarchical`)。
2 使用 CrewAI 的智能体定义方式
Systems_Analyst = Agent(
role="系统分析师",
goal="按照用户提出的任务,进行需求分析,撰写系统需求报告",
backstory="您在一家专业设计企业工作。...",
verbose=False,
allow_delegation=True,
step_callback=agent_step_callback,
)
```
- `role`、`goal`、`backstory` 是 CrewAI 智能体的标准属性,用于定义智能体的身份和行为准则。
- `allow_delegation`:允许智能体将任务委托给其他智能体(在 CrewAI 中实现协作)。
- `step_callback`:CrewAI 提供的回调钩子,可在每个执行步骤后获取中间输出。
3 使用 CrewAI 的任务定义方式
Systems_Analyst_task = Task(
description=query,
expected_output="系统需求报告,OUT IN CHINESE",
agent=Systems_Analyst,
)
```
- `description`:任务的具体指令。
- `expected_output`:期望的输出格式,CrewAI 会用此指导智能体生成。
- `agent`:指定执行该任务的智能体。
4 使用 CrewAI 的 Crew 组建方式
crew = Crew(
agents=[Systems_Analyst, Designer],
tasks=[Systems_Analyst_task, Designer_task],
verbose=1,
process=Process.sequential,
max_iter=5,
)
```
- `agents` 和 `tasks` 列表定义了团队成员和任务。
- `process` 指定执行流程,这里为顺序执行(一个任务完成后才启动下一个)。
- `verbose` 控制日志输出级别。
- `max_iter` 限制最大迭代次数,防止无限循环。
5 执行 Crew
result = crew.kickoff()
```
`kickoff()` 是 CrewAI 启动多智能体协作的标准方法,返回最终结果。
6 回调机制捕获中间步骤
def agent_step_callback(step_output):
for step in step_output:
if isinstance(step, tuple) and len(step) == 2:
if isinstance(step[0], AgentAction):
if not step[0].tool == "_Exception":
q.put("## " + step[0].tool)
q.put("### 执行过程")
q.put(step[0].log)
q.put("### 执行结果")
q.put(step[1])
```
- `step_callback` 是 CrewAI 提供的回调接口,参数 `step_output` 包含当前步骤的详细信息,包括 `AgentAction`(工具调用信息)和结果。
- 这里将每个工具调用的名称、日志和结果放入队列,实现流式输出。
7 环境变量配置
os.environ["OPENAI_API_BASE"] = "http://server-dev:8000/v1"
os.environ["OPENAI_MODEL_NAME"] = "glm-4-9b-chat"
os.environ["OPENAI_API_KEY"] = "EMPTY"
```
CrewAI 默认使用 OpenAI 风格的 API,通过环境变量配置即可无缝接入任何兼容的模型(包括本地部署的模型),这是 CrewAI 的常见用法。
8.Haystack
通过模块化设计,将文档存储、检索、阅读/生成等环节解耦为独立组件,通过组件+管道搭建流水线,可启动日志,和开源Langfuse集成。
1 导入路径:
from haystack import Pipeline、from haystack.nodes import ... 等,明确使用 haystack 包。
---
2 核心类使用:
document_store = InMemoryDocumentStore()
documents = [
{"content": "Haystack 是一个用于构建 RAG 应用的开源框架。"},
{"content": "它支持多种文档存储后端,如 Elasticsearch、FAISS。"}
]
document_store.write_documents(documents)
retriever = BM25Retriever(document_store=document_store)
prompt_node = PromptNode(
model_name_or_path="gpt-4",
api_key="EMPTY",
default_prompt_template="根据上下文回答问题:{documents}\n问题:{query}"
)
---
InMemoryDocumentStore(文档存储)
BM25Retriever(检索器)
PromptNode(提示节点,用于生成)
---
3 Pipeline 构建方式
pipeline = Pipeline()
pipeline.add_node(component=retriever, name="Retriever", inputs=["Query"])
pipeline.add_node(component=prompt_node, name="PromptNode", inputs=["Retriever"])
result = pipeline.run(query="Haystack 支持哪些存储后端?")
print_answers(result, details="minimum")
---
add_node() 方法串联组件,并指定 inputs 和 name,这是 Haystack 的典型 Pipeline 模式。
print_answers:Haystack 提供的辅助函数,用于格式化输出。
9.CopilotKit
前后端分离架构,让开发者可以快速为应用添加对话式AI能力,并让AI能够直接操作应用界面(特别是React应用)。
1 导入前缀
'''
"use client";
import { CopilotKit } from "@copilotkit/react-core";
import { useCopilotAction, useCopilotReadable } from "@copilotkit/react-core";
import { CopilotSidebar } from "@copilotkit/react-ui";
import "@copilotkit/react-ui/styles.css";
'''
@copilotkit/react-core、@copilotkit/react-ui、@copilotkit/runtime 等,全部以 @copilotkit 开头。
---
2 核心组件/Hooks:
'''
export default function Home() {
return (
<CopilotKit runtimeUrl="/api/copilotkit/">
<CopilotSidebar defaultOpen={true}>
<TodoList />
</CopilotSidebar>
</CopilotKit>
);
}
function TodoList() {
const [todos, setTodos] = useState<string[]>([]);
useCopilotReadable({
description: "当前的待办事项列表",
value: todos
});
useCopilotAction({
name: "addTodo",
description: "添加一个新的待办事项",
parameters: [
{
name: "item",
type: "string",
description: "待办事项的内容",
required: true
}
],
handler: ({ item }) => {
setTodos((prev) => [...prev, item]);
}
});
return (
<div>
<ul>
{todos.map((todo, i) => <li key={i}>{todo}</li>)}
</ul>
</div>
);
}
'''
<CopilotKit> 根组件
useCopilotAction:定义 AI 可执行的操作
useCopilotReadable:向 AI 暴露状态
<CopilotSidebar>:内置的聊天界面组件
---
3 后端 API 路由(app/api/copilotkit/route.ts)
'''
import { CopilotRuntime } from "@copilotkit/runtime";
import { LangChainAdapter } from "@copilotkit/runtime-langchain";
import { ChatOpenAI } from "@langchain/openai";
const runtime = new CopilotRuntime({
actions: [],
remoteActions: []
});
export async function POST(req: Request) {
const model = new ChatOpenAI({
modelName: "glm-4-9b-chat",
configuration: {
baseURL: "http://server-dev:8000/v1",
}
});
const langchainAdapter = new LangChainAdapter({ model });
return runtime.handleRequest(req, langchainAdapter);
}
'''
后端集成:CopilotRuntime + LangChainAdapter,将 LangChain 模型与 CopilotKit 连接。
API 路由路径:通常位于 app/api/copilotkit/route.ts(Next.js App Router),这是 CopilotKit 推荐的默认位置。
FastAPI,Express.js,Flask,Django,Streamlit,Gradio,React,Vue 3 + Vite,UniApp,Tauri等前后端框架就不写了。
补充:Function Calling,MCP,Skills三者的关系和讲解:
Function Calling,MCP,Skills三者的关系和讲解:
假设我们有一个任务:让 AI 查询 GitHub 仓库信息并生成周报
在Function Calling出现前,我们直接把这个任务给LLM,它很可能会出现幻觉,胡编乱造,因为它不知道今天的日期,也不知道 GitHub 仓库目前有多少星。
想要拿到周报,很明显有两个选项:
1.我们自己去获取这些新的数据,收集后发给它,共同完成这个任务;
2.写一点代码,让LLM去调用外部系统 (GitHub API、数据库、文件系统)的接口获取数据。
Function Calling:
'''
# 1. 定义工具说明书 (这是给 LLM 看的)
tools = [
{
"type": "function",
"function": {
"name": "get_github_stars",
"description": "获取指定 GitHub 仓库的 Star 数量",
"parameters": {
"type": "object",
"properties": {
"repo_name": {"type": "string", "description": "仓库名,格式如 'owner/repo'"}
},
"required": ["repo_name"]
}
}
}
]
# 2. 用户提问
user_query = "帮我看看 microsoft/vscode 有多少星?"
# 3. LLM 思考 (伪代码)
# LLM 收到 query 和 tools,它不会直接回答数字,而是返回:
llm_response = {
"role": "assistant",
"content": "",
"tool_calls": [
{
"id": "call_123",
"function": {
"name": "get_github_stars",
"arguments": '{"repo_name": "microsoft/vscode"}' # 结构化数据!
}
}
]
}
# 4. 执行实际逻辑
if llm_response.tool_calls:
func_name = llm_response.tool_calls[0].function.name
args = json.loads(llm_response.tool_calls[0].function.arguments)
if func_name == "get_github_stars":
# 这里才是真正去请求 GitHub API 的地方
stars = real_github_api_request(args['repo_name'])
print(f"VSCode 有 {stars} 个星")
'''
问题是:Function Calling 只是让 LLM 决定调用什么,但怎么调用(发 HTTP 请求、处理鉴权、解析错误)还得自己写代码硬编码。
那么既然是调用外部的接口,能不能由外部那边都按照一个样式,统一写好,这样无论我们是调用github的工具还是高德等第三方工具,都只需修改少量代码,不用写这么多了。
MCP (Model Context Protocol):
MCP 定义了一种标准协议。
MCP Server:由第三方(如 GitHub 官方或社区)写好,专门负责跟 GitHub API 打交道,暴露标准接口。
MCP Client:你的 LLM 应用只需连接 MCP Client,就能自动发现所有可用的工具,无需硬编码。
没有 MCP:
LLM App --(硬编码 HTTP 请求)--> GitHub API
LLM App --(硬编码 HTTP 请求)--> Slack API
LLM App --(硬编码 SQL)--> MySQL
有了 MCP:
LLM App (MCP Client) <==标准协议==> MCP Host
⬇️ 自动发现工具列表
├── GitHub MCP Server (负责对接 GitHub)
├── Slack MCP Server (负责对接 Slack)
└── Filesystem MCP Server (负责读写文件)
不再需要写 get_github_stars 的具体实现,而是配置 MCP Client:
'''
# 初始化 MCP Client,连接到本地的 MCP Server 集群
client = MCPClient()
client.connect("stdio://github-mcp-server") # 启动 GitHub 的 MCP 服务
client.connect("stdio://filesystem-mcp-server") # 启动文件服务
# 自动获取所有可用工具 (GitHub 的服务器会自动注册 get_stars, list_issues 等)
available_tools = client.list_tools()
# 输出: [{'name': 'github_get_stars', ...}, {'name': 'fs_read_file', ...}]
# 把自动获取的工具传给 LLM (同 Function Calling 流程)
response = llm.chat(user_query, tools=available_tools)
# 当 LLM 决定调用 github_get_stars 时,MCP Client 自动转发请求给 GitHub Server
# 开发者无需关心具体的 HTTP 签名和 URL 构造
result = client.call_tool(response.tool_call)
'''
问题点:“生成周报”,这需要:查 Git 提交 -> 查 Jira 任务 -> 总结 -> 格式化 -> 发邮件。单靠 LLM 自动匹配容易漏步骤,写成Prompt+MCP 工具硬编码也很麻烦。
能不能更优化一些,Prompt+MCP工具能不能用自然语言描述,不再硬编码。
Skills:
Skills 是一个打包好的配置文件(通常是 Markdown 或 YAML)。它定义了:
触发词:用户说什么时激活?
依赖工具:需要调用哪些 MCP 工具?
执行剧本 (Workflow):第一步做什么,第二步做什么,中间如何处理数据。
'''
Skills 文件示例 (weekly_report_skill.md)
# Skill: 生成研发周报
## 触发指令
当用户要求“生成周报”或“总结本周工作”时激活。
## 可用工具 (通过 MCP 加载)
- github_mcp: get_commits, list_pull_requests
- jira_mcp: search_tickets
## 执行流程 (LLM 需严格遵守)
1. **获取时间范围**:询问用户或默认为本周一至今。
2. **拉取数据**:
- 调用 `github_mcp.get_commits` 获取代码提交记录。
- 调用 `jira_mcp.search_tickets` 获取状态为 "Done" 的任务。
3. **分析与总结**:
- 将提交记录按模块分类。
- 关联 Jira 任务号。
- 用简练的语言总结产出。
4. **输出格式**:
- 按照 Markdown 表格输出:| 模块 | 完成内容 | 关联任务 |
- 最后附上“下周计划建议”。
## 注意事项
- 如果没有数据,不要编造,直接回复“本周暂无记录”。
'''
总结:Skills识别出需要执行的技能,技能中的每一步通过MCP调用具体系统/工具,MCP底层依赖Function Calling实现函数的调用和数据的获取。
更多推荐



所有评论(0)