找回密码
 立即注册
首页 业界区 业界 “Pocket Flow,一个仅用 100 行代码实现的 LLM 框架” ...

“Pocket Flow,一个仅用 100 行代码实现的 LLM 框架”

撵延兵 2025-6-2 23:21:41
PocketFlow介绍

PocketFlow是我最近在探索的一个LLM 框架,我觉得很有意思,因此推荐给大家。
这个框架最大的特点就是:“Pocket Flow,一个仅用 100 行代码实现的 LLM 框架”。
我很好奇,一个框架只有100行代码是怎么做到的,它又有什么魅力呢?
正如作者所言现在的LLM框架过于臃肿了!
在使用各种框架的过程中,你可能会有如下的感觉:

  • 臃肿的抽象:正如 Octomind 的工程团队所解释的:“LangChain 在最初对我们简单的功能需求与它的使用假设相匹配时很有帮助。但其高级抽象很快使我们的代码更难以理解并令人沮丧地难以维护。”这些框架通过不必要的复杂性隐藏了简单功能。
  • 实现噩梦:除了抽象之外,这些框架还给开发者带来了依赖项臃肿、版本冲突和不断变化的接口的负担。开发者经常抱怨:“它不稳定,接口不断变化,文档经常过时。”另一个开发者开玩笑说:“在读这句话的时间内,LangChain 已经弃用了 4 个类而没有更新文档。”
PocketFlow作者开始思考这个问题:“我们真的需要这么多的包装器吗?如果我们去掉一切会怎样?什么是真正最小且可行的?
PocketFlow作者在过去一年从零开始构建 LLM 应用程序后,有了一个顿悟:在所有复杂性之下,LLM 系统本质上只是简单的有向图。通过去除不必要的层,他创建了 Pocket Flow——一个没有任何冗余、没有任何依赖、没有任何供应商锁定的框架,全部代码仅 100 行。
1.webp

AI 系统框架的抽象、应用特定封装、供应商特定封装、代码行数和大小的比较。
来源:https://pocketflow.substack.com/p/i-built-an-llm-framework-in-just
GitHub地址:https://github.com/The-Pocket/PocketFlow
2.png

PocketFlow的构建块

flowchart TD    id1[PocketFlow] -->b[Node] & c[Flow] & D[Shared Store]
3.png

理解PocketFlow需要理解Node、Flow与Shared Store这三个基本的概念。
想象 Pocket Flow 就像一个井然有序的厨房:

  • Node就像烹饪站(切菜、烹饪、摆盘)
  • Flow就像食谱,指示下一步访问哪个站台。
  • Shared Store是所有工作站都能看到原料的台面。
在我们的厨房(代理系统),每个站点(Node)执行三个简单的操作:

  • Prep: 从共享存储中获取你需要的东西(收集原料)
  • Exec: 执行你的专门任务(烹饪原料)
  • Post: 将结果返回到共享存储并确定下一步行动(上菜并决定下一步做什么)
flowchart TD    id1[Node] -->b[Prep] & c[Exec] & D[Post]
4.png

食谱 (Flow) 依据条件 (Orch) 指导执行:

  • “如果蔬菜被切碎,前往烹饪站”
  • “如果饭菜做好了,移到装盘站“
PocketFlow还支持批处理、异步执行和并行处理,适用于节点和流程。就是这样!这就是构建LLM应用程序所需的一切。没有不必要的抽象,没有复杂的架构——只有简单的构建块,可以组合成强大的系统。
5.webp

Pocket Flow 核心图抽象
来源:https://pocketflow.substack.com/p/i-built-an-llm-framework-in-just
PocketFlow作者介绍

Zachary Huang:即将加入微软研究院AI前沿研究。目前从事大规模语言模型代理和系统的研究。喜欢构建、写作和制作梗图。之前经历:哥伦比亚大学博士,微软Gray Systems Lab,Databricks,2023年谷歌博士奖学金。
6.png

大佬不仅代码厉害还喜欢写通俗易懂的文章,最近看完了大佬的所有文章,感谢大佬的贡献,感兴趣的朋友也可以去看看。
7.png

地址:https://pocketflow.substack.com
PocketFlow实践

直接上手学习大佬提供的cookbook即可。
这里就演示一下几个入门的demo。
pocketflow-hello-world

定义Node与Flow:
  1. from pocketflow import Node, Flow
  2. from utils.call_llm import call_llm
  3. # An example node and flow
  4. # Please replace this with your own node and flow
  5. class AnswerNode(Node):
  6.     def prep(self, shared):
  7.         # Read question from shared
  8.         return shared["question"]
  9.    
  10.     def exec(self, question):
  11.         return call_llm(question)
  12.    
  13.     def post(self, shared, prep_res, exec_res):
  14.         # Store the answer in shared
  15.         shared["answer"] = exec_res
  16. answer_node = AnswerNode()
  17. qa_flow = Flow(start=answer_node)
复制代码
主脚本写了Shared Store:
  1. from flow import qa_flow
  2. # Example main function
  3. # Please replace this with your own main function
  4. def main():
  5.     shared = {
  6.         "question": "你是谁?",
  7.         "answer": None
  8.     }
  9.     qa_flow.run(shared)
  10.     print("Question:", shared["question"])
  11.     print("Answer:", shared["answer"])
  12. if __name__ == "__main__":
  13.     main()
复制代码
call_llm可以改成这样:
  1. from openai import OpenAI
  2. import os
  3. def call_llm(prompt):
  4.     client = OpenAI(api_key="your api key",
  5.                     base_url="https://api.siliconflow.cn/v1")
  6.    
  7.     response = client.chat.completions.create(
  8.         model="Qwen/Qwen2.5-72B-Instruct",
  9.         messages=[{"role": "user", "content": prompt}],
  10.         temperature=0.7
  11.     )
  12.    
  13.     return response.choices[0].message.content
  14. if __name__ == "__main__":
  15.     # Test the LLM call
  16.     messages = [{"role": "user", "content": "In a few words, what's the meaning of life?"}]
  17.     response = call_llm(messages)
  18.     print(f"Prompt: {messages[0]['content']}")
  19.     print(f"Response: {response}")
复制代码
效果:
8.png

pocketflow-chat

flowchart LR    chat[ChatNode] -->|continue| chat
9.png
  1. from pocketflow import Node, Flow
  2. from utils import call_llm
  3. class ChatNode(Node):
  4.     def prep(self, shared):
  5.         # Initialize messages if this is the first run
  6.         if "messages" not in shared:
  7.             shared["messages"] = []
  8.             print("Welcome to the chat! Type 'exit' to end the conversation.")
  9.         
  10.         # Get user input
  11.         user_input = input("\nYou: ")
  12.         
  13.         # Check if user wants to exit
  14.         if user_input.lower() == 'exit':
  15.             return None
  16.         
  17.         # Add user message to history
  18.         shared["messages"].append({"role": "user", "content": user_input})
  19.         
  20.         # Return all messages for the LLM
  21.         return shared["messages"]
  22.     def exec(self, messages):
  23.         if messages is None:
  24.             return None
  25.         
  26.         # Call LLM with the entire conversation history
  27.         response = call_llm(messages)
  28.         return response
  29.     def post(self, shared, prep_res, exec_res):
  30.         if prep_res is None or exec_res is None:
  31.             print("\nGoodbye!")
  32.             return None  # End the conversation
  33.         
  34.         # Print the assistant's response
  35.         print(f"\nAssistant: {exec_res}")
  36.         
  37.         # Add assistant message to history
  38.         shared["messages"].append({"role": "assistant", "content": exec_res})
  39.         
  40.         # Loop back to continue the conversation
  41.         return "continue"
  42. # Create the flow with self-loop
  43. chat_node = ChatNode()
  44. chat_node - "continue" >> chat_node  # Loop back to continue conversation
  45. flow = Flow(start=chat_node)
  46. # Start the chat
  47. if __name__ == "__main__":
  48.     shared = {}
  49.     flow.run(shared)
复制代码
效果:
10.png

pocketflow-chat-guardrail

flowchart LR    user[UserInputNode] -->|validate| guardrail[GuardrailNode]    guardrail -->|retry| user    guardrail -->|process| llm[LLMNode]    llm -->|continue| user
11.png


  • 一个 UserInputNode,其 exec 方法收集用户输入
  • 一个GuardrailNode,用于验证查询是否与旅行相关,使用:

    • 基本验证检查(空输入、过短)
    • 基于 LLM 的验证,以确定查询是否与旅行相关

  • 一个 LLMNode,使用带旅行顾问系统提示的LLM处理有效的旅行查询
  • 流连接,在处理之前通过验证路由输入,并处理与旅行无关查询的重复尝试
  1. from pocketflow import Node, Flow
  2. from utils import call_llm
  3. class UserInputNode(Node):
  4.     def prep(self, shared):
  5.         # Initialize messages if this is the first run
  6.         if "messages" not in shared:
  7.             shared["messages"] = []
  8.             print("Welcome to the Travel Advisor Chat! Type 'exit' to end the conversation.")
  9.         
  10.         return None
  11.     def exec(self, _):
  12.         # Get user input
  13.         user_input = input("\nYou: ")
  14.         return user_input
  15.     def post(self, shared, prep_res, exec_res):
  16.         user_input = exec_res
  17.         
  18.         # Check if user wants to exit
  19.         if user_input and user_input.lower() == 'exit':
  20.             print("\nGoodbye! Safe travels!")
  21.             return None  # End the conversation
  22.         
  23.         # Store user input in shared
  24.         shared["user_input"] = user_input
  25.         
  26.         # Move to guardrail validation
  27.         return "validate"
  28. class GuardrailNode(Node):
  29.     def prep(self, shared):
  30.         # Get the user input from shared data
  31.         user_input = shared.get("user_input", "")
  32.         return user_input
  33.    
  34.     def exec(self, user_input):
  35.         # Basic validation checks
  36.         if not user_input or user_input.strip() == "":
  37.             return False, "Your query is empty. Please provide a travel-related question."
  38.         
  39.         if len(user_input.strip()) < 3:
  40.             return False, "Your query is too short. Please provide more details about your travel question."
  41.         
  42.         # LLM-based validation for travel topics
  43.         prompt = f"""
  44. Evaluate if the following user query is related to travel advice, destinations, planning, or other travel topics.
  45. The chat should ONLY answer travel-related questions and reject any off-topic, harmful, or inappropriate queries.
  46. User query: {user_input}
  47. Return your evaluation in YAML format:
  48. ```yaml
  49. valid: true/false
  50. reason: [Explain why the query is valid or invalid]
  51. ```"""
  52.         
  53.         # Call LLM with the validation prompt
  54.         messages = [{"role": "user", "content": prompt}]
  55.         response = call_llm(messages)
  56.         
  57.         # Extract YAML content
  58.         yaml_content = response.split("```yaml")[1].split("```")[0].strip() if "```yaml" in response else response
  59.         
  60.         import yaml
  61.         result = yaml.safe_load(yaml_content)
  62.         assert result is not None, "Error: Invalid YAML format"
  63.         assert "valid" in result and "reason" in result, "Error: Invalid YAML format"
  64.         is_valid = result.get("valid", False)
  65.         reason = result.get("reason", "Missing reason in YAML response")
  66.         
  67.         return is_valid, reason
  68.    
  69.     def post(self, shared, prep_res, exec_res):
  70.         is_valid, message = exec_res
  71.         
  72.         if not is_valid:
  73.             # Display error message to user
  74.             print(f"\nTravel Advisor: {message}")
  75.             # Skip LLM call and go back to user input
  76.             return "retry"
  77.         
  78.         # Valid input, add to message history
  79.         shared["messages"].append({"role": "user", "content": shared["user_input"]})
  80.         # Proceed to LLM processing
  81.         return "process"
  82. class LLMNode(Node):
  83.     def prep(self, shared):
  84.         # Add system message if not present
  85.         if not any(msg.get("role") == "system" for msg in shared["messages"]):
  86.             shared["messages"].insert(0, {
  87.                 "role": "system",
  88.                 "content": "You are a helpful travel advisor that provides information about destinations, travel planning, accommodations, transportation, activities, and other travel-related topics. Only respond to travel-related queries and keep responses informative and friendly. Your response are concise in 100 words."
  89.             })
  90.         
  91.         # Return all messages for the LLM
  92.         return shared["messages"]
  93.     def exec(self, messages):
  94.         # Call LLM with the entire conversation history
  95.         response = call_llm(messages)
  96.         return response
  97.     def post(self, shared, prep_res, exec_res):
  98.         # Print the assistant's response
  99.         print(f"\nTravel Advisor: {exec_res}")
  100.         
  101.         # Add assistant message to history
  102.         shared["messages"].append({"role": "assistant", "content": exec_res})
  103.         
  104.         # Loop back to continue the conversation
  105.         return "continue"
  106. # Create the flow with nodes and connections
  107. user_input_node = UserInputNode()
  108. guardrail_node = GuardrailNode()
  109. llm_node = LLMNode()
  110. # Create flow connections
  111. user_input_node - "validate" >> guardrail_node
  112. guardrail_node - "retry" >> user_input_node  # Loop back if input is invalid
  113. guardrail_node - "process" >> llm_node
  114. llm_node - "continue" >> user_input_node     # Continue conversation
  115. flow = Flow(start=user_input_node)
  116. # Start the chat
  117. if __name__ == "__main__":
  118.     shared = {}
  119.     flow.run(shared)
复制代码
效果:
12.png

最后

PocketFlow还有很多有趣的例子,感兴趣的朋友可以自己去试试!!
但是说实话PocketFlow的“易用性”还是不足的,没法像很多框架那样开箱即用,还是需要自己写很多代码的,但也就是它的小巧给了它很大的灵活性,开发者可以根据自己的想法灵活地去写程序。

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
您需要登录后才可以回帖 登录 | 立即注册