codegnipy 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,244 @@
1
+ """
2
+ Codegnipy 反思模块
3
+
4
+ 实现反思循环,让 LLM 能够检查和修正自己的输出。
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional, Callable, List
9
+ from enum import Enum
10
+
11
+ from .runtime import cognitive_call, CognitiveContext
12
+
13
+
14
+ class ReflectionStatus(Enum):
15
+ """反思状态"""
16
+ PASSED = "passed" # 通过验证
17
+ NEEDS_FIX = "needs_fix" # 需要修正
18
+ FAILED = "failed" # 修正失败
19
+
20
+
21
+ @dataclass
22
+ class ReflectionResult:
23
+ """反思结果"""
24
+ status: ReflectionStatus
25
+ original_response: str
26
+ corrected_response: Optional[str] = None
27
+ issues: List[str] = field(default_factory=list)
28
+ suggestions: List[str] = field(default_factory=list)
29
+ iterations: int = 1
30
+
31
+
32
+ class Reflector:
33
+ """
34
+ 反思器:让 LLM 检查和修正自己的输出
35
+
36
+ 工作流程:
37
+ 1. 生成初始响应
38
+ 2. 自我审查,识别问题
39
+ 3. 根据问题进行修正
40
+ 4. 重复直到通过或达到最大迭代次数
41
+ """
42
+
43
+ DEFAULT_CRITIQUE_PROMPT = """请审查以下回答,检查是否存在问题:
44
+
45
+ 原始问题: {prompt}
46
+ 回答: {response}
47
+
48
+ 请从以下角度检查:
49
+ 1. 事实准确性:是否有错误或误导性信息
50
+ 2. 完整性:是否完整回答了问题
51
+ 3. 清晰度:表达是否清晰易懂
52
+ 4. 格式:格式是否符合要求
53
+
54
+ 如果存在问题,请列出具体问题。如果回答良好,请回复 "PASSED"。
55
+ """
56
+
57
+ DEFAULT_FIX_PROMPT = """请修正以下回答中的问题:
58
+
59
+ 原始问题: {prompt}
60
+ 原回答: {response}
61
+ 发现的问题: {issues}
62
+
63
+ 请提供修正后的回答。"""
64
+
65
+ def __init__(
66
+ self,
67
+ max_iterations: int = 3,
68
+ critique_prompt: Optional[str] = None,
69
+ fix_prompt: Optional[str] = None,
70
+ validator: Optional[Callable[[str], bool]] = None
71
+ ):
72
+ """
73
+ 参数:
74
+ max_iterations: 最大反思迭代次数
75
+ critique_prompt: 审查提示模板
76
+ fix_prompt: 修正提示模板
77
+ validator: 自定义验证函数
78
+ """
79
+ self.max_iterations = max_iterations
80
+ self.critique_prompt = critique_prompt or self.DEFAULT_CRITIQUE_PROMPT
81
+ self.fix_prompt = fix_prompt or self.DEFAULT_FIX_PROMPT
82
+ self.validator = validator
83
+
84
+ def reflect(
85
+ self,
86
+ prompt: str,
87
+ initial_response: str,
88
+ context: Optional[CognitiveContext] = None
89
+ ) -> ReflectionResult:
90
+ """
91
+ 执行反思循环
92
+
93
+ 参数:
94
+ prompt: 原始用户提示
95
+ initial_response: 初始响应
96
+ context: 认知上下文
97
+ 返回:
98
+ ReflectionResult 对象
99
+ """
100
+ current_response = initial_response
101
+ issues: List[str] = []
102
+
103
+ for iteration in range(self.max_iterations):
104
+ # 审查当前响应
105
+ critique = self._critique(prompt, current_response, context)
106
+
107
+ if critique.strip().upper().startswith("PASSED"):
108
+ # 通过审查
109
+ return ReflectionResult(
110
+ status=ReflectionStatus.PASSED,
111
+ original_response=initial_response,
112
+ corrected_response=current_response if iteration > 0 else None,
113
+ iterations=iteration + 1
114
+ )
115
+
116
+ # 提取问题
117
+ found_issues = self._extract_issues(critique)
118
+ issues.extend(found_issues)
119
+
120
+ # 修正响应
121
+ current_response = self._fix(
122
+ prompt, current_response, found_issues, context
123
+ )
124
+
125
+ # 自定义验证
126
+ if self.validator is not None and self.validator(current_response):
127
+ return ReflectionResult(
128
+ status=ReflectionStatus.PASSED,
129
+ original_response=initial_response,
130
+ corrected_response=current_response,
131
+ issues=issues,
132
+ iterations=iteration + 1
133
+ )
134
+
135
+ # 达到最大迭代次数
136
+ return ReflectionResult(
137
+ status=ReflectionStatus.NEEDS_FIX if current_response != initial_response else ReflectionStatus.FAILED,
138
+ original_response=initial_response,
139
+ corrected_response=current_response,
140
+ issues=issues,
141
+ iterations=self.max_iterations
142
+ )
143
+
144
+ def _critique(
145
+ self,
146
+ prompt: str,
147
+ response: str,
148
+ context: Optional[CognitiveContext]
149
+ ) -> str:
150
+ """审查响应"""
151
+ critique_prompt = self.critique_prompt.format(
152
+ prompt=prompt,
153
+ response=response
154
+ )
155
+ return cognitive_call(critique_prompt, context)
156
+
157
+ def _fix(
158
+ self,
159
+ prompt: str,
160
+ response: str,
161
+ issues: List[str],
162
+ context: Optional[CognitiveContext]
163
+ ) -> str:
164
+ """修正响应"""
165
+ fix_prompt = self.fix_prompt.format(
166
+ prompt=prompt,
167
+ response=response,
168
+ issues="\n".join(f"- {i}" for i in issues)
169
+ )
170
+ return cognitive_call(fix_prompt, context)
171
+
172
+ def _extract_issues(self, critique: str) -> List[str]:
173
+ """从审查结果中提取问题"""
174
+ issues = []
175
+ lines = critique.strip().split("\n")
176
+
177
+ for line in lines:
178
+ line = line.strip()
179
+ # 跳过 "PASSED" 标记和空行
180
+ if not line or line.upper().startswith("PASSED"):
181
+ continue
182
+
183
+ # 提取以数字或破折号开头的问题
184
+ if line[0].isdigit() or line.startswith("-"):
185
+ # 移除序号前缀
186
+ issue = line.lstrip("0123456789.-) ").strip()
187
+ if issue:
188
+ issues.append(issue)
189
+
190
+ return issues if issues else [critique]
191
+
192
+
193
+ def with_reflection(
194
+ prompt: str,
195
+ context: Optional[CognitiveContext] = None,
196
+ max_iterations: int = 3,
197
+ validator: Optional[Callable[[str], bool]] = None
198
+ ) -> ReflectionResult:
199
+ """
200
+ 带反思的便捷函数
201
+
202
+ 示例:
203
+ with CognitiveContext(api_key="sk-...") as ctx:
204
+ result = with_reflection(
205
+ "解释量子纠缠",
206
+ context=ctx,
207
+ max_iterations=2
208
+ )
209
+ print(result.corrected_response or result.original_response)
210
+ """
211
+ # 生成初始响应
212
+ initial = cognitive_call(prompt, context)
213
+
214
+ # 创建反思器
215
+ reflector = Reflector(max_iterations=max_iterations, validator=validator)
216
+
217
+ # 执行反思
218
+ return reflector.reflect(prompt, initial, context)
219
+
220
+
221
+ class ReflectiveCognitiveCall:
222
+ """
223
+ 带反思的认知调用类
224
+
225
+ 使用方式:
226
+ call = ReflectiveCognitiveCall(max_iterations=2)
227
+ with CognitiveContext() as ctx:
228
+ response = call("解释量子纠缠", context=ctx)
229
+ """
230
+
231
+ def __init__(self, max_iterations: int = 3, validator: Optional[Callable[[str], bool]] = None):
232
+ self.max_iterations = max_iterations
233
+ self.validator = validator
234
+ self.reflector = Reflector(max_iterations, validator=validator)
235
+
236
+ def __call__(self, prompt: str, context: Optional[CognitiveContext] = None) -> str:
237
+ """执行带反思的调用"""
238
+ initial = cognitive_call(prompt, context)
239
+ result = self.reflector.reflect(prompt, initial, context)
240
+
241
+ # 返回最佳响应
242
+ if result.status == ReflectionStatus.FAILED:
243
+ return initial # 失败时返回原始响应
244
+ return result.corrected_response or result.original_response
codegnipy/runtime.py ADDED
@@ -0,0 +1,197 @@
1
+ """
2
+ Codegnipy 运行时核心模块
3
+
4
+ 提供 cognitive_call 函数和 CognitiveContext 上下文管理器。
5
+ """
6
+
7
+ from dataclasses import dataclass, field
8
+ from typing import Optional, TYPE_CHECKING
9
+ import os
10
+
11
+ if TYPE_CHECKING:
12
+ from .memory import MemoryStore
13
+
14
+
15
+ @dataclass
16
+ class LLMConfig:
17
+ """LLM 配置"""
18
+ api_key: Optional[str] = None
19
+ model: str = "gpt-4o-mini"
20
+ base_url: Optional[str] = None
21
+ temperature: float = 0.7
22
+ max_tokens: int = 1024
23
+
24
+
25
+ @dataclass
26
+ class CognitiveContext:
27
+ """
28
+ 认知上下文管理器
29
+
30
+ 管理 LLM 配置、会话记忆和调用追踪。
31
+
32
+ 示例:
33
+ with CognitiveContext(api_key="sk-...") as ctx:
34
+ result = cognitive_call("翻译:你好")
35
+ """
36
+ api_key: Optional[str] = None
37
+ model: str = "gpt-4o-mini"
38
+ base_url: Optional[str] = None
39
+ temperature: float = 0.7
40
+ max_tokens: int = 1024
41
+
42
+ # 记忆存储(可以是 InMemoryStore 或 FileStore)
43
+ memory_store: Optional["MemoryStore"] = field(default=None, repr=False)
44
+
45
+ # 内部状态
46
+ _is_active: bool = field(default=False, repr=False, init=False)
47
+
48
+ # 全局上下文引用
49
+ _current: Optional['CognitiveContext'] = field(default=None, repr=False, init=False)
50
+
51
+ def __post_init__(self):
52
+ """初始化记忆存储"""
53
+ if self.memory_store is None:
54
+ from .memory import InMemoryStore
55
+ self.memory_store = InMemoryStore()
56
+
57
+ def __enter__(self) -> 'CognitiveContext':
58
+ self._is_active = True
59
+ CognitiveContext._current = self
60
+ return self
61
+
62
+ def __exit__(self, exc_type, exc_val, exc_tb):
63
+ self._is_active = False
64
+ CognitiveContext._current = None
65
+ return False
66
+
67
+ @classmethod
68
+ def get_current(cls) -> Optional['CognitiveContext']:
69
+ """获取当前活动上下文"""
70
+ return cls._current
71
+
72
+ def get_config(self) -> LLMConfig:
73
+ """获取 LLM 配置"""
74
+ api_key = self.api_key or os.environ.get("OPENAI_API_KEY")
75
+ return LLMConfig(
76
+ api_key=api_key,
77
+ model=self.model,
78
+ base_url=self.base_url,
79
+ temperature=self.temperature,
80
+ max_tokens=self.max_tokens
81
+ )
82
+
83
+ def add_to_memory(self, role: str, content: str) -> None:
84
+ """添加到会话记忆"""
85
+ from .memory import Message, MessageRole
86
+ role_enum = MessageRole(role) if isinstance(role, str) else role
87
+ # 直接操作内存存储
88
+ if self.memory_store is not None and hasattr(self.memory_store, '_messages'):
89
+ self.memory_store._messages.append(Message(role_enum, content))
90
+
91
+ def get_memory(self) -> list:
92
+ """获取会话记忆(OpenAI 格式)"""
93
+ if self.memory_store is not None:
94
+ return self.memory_store.to_openai_messages()
95
+ return []
96
+
97
+ def get_memory_store(self) -> Optional["MemoryStore"]:
98
+ """获取记忆存储对象"""
99
+ return self.memory_store
100
+
101
+ def clear_memory(self) -> None:
102
+ """清空会话记忆"""
103
+ if self.memory_store is not None:
104
+ self.memory_store.clear()
105
+
106
+
107
+ def _call_openai(config: LLMConfig, prompt: str, memory: Optional[list] = None) -> str:
108
+ """调用 OpenAI API"""
109
+ try:
110
+ import openai
111
+ except ImportError:
112
+ raise ImportError(
113
+ "需要安装 openai 包。运行: pip install openai"
114
+ )
115
+
116
+ client = openai.OpenAI(
117
+ api_key=config.api_key,
118
+ base_url=config.base_url
119
+ )
120
+
121
+ messages: list = []
122
+ if memory:
123
+ messages.extend(memory)
124
+ messages.append({"role": "user", "content": prompt})
125
+
126
+ response = client.chat.completions.create(
127
+ model=config.model,
128
+ messages=messages,
129
+ temperature=config.temperature,
130
+ max_tokens=config.max_tokens
131
+ )
132
+
133
+ return response.choices[0].message.content or ""
134
+
135
+
136
+ def cognitive_call(
137
+ prompt: str,
138
+ context: Optional[CognitiveContext] = None,
139
+ *,
140
+ model: Optional[str] = None,
141
+ temperature: Optional[float] = None
142
+ ) -> str:
143
+ """
144
+ 执行认知调用(调用 LLM)
145
+
146
+ 这是 `~"prompt"` 语法的运行时实现。
147
+
148
+ 参数:
149
+ prompt: 发送给 LLM 的提示
150
+ context: 认知上下文(可选,默认使用当前活动上下文)
151
+ model: 覆盖上下文中的模型设置
152
+ temperature: 覆盖上下文中的温度设置
153
+
154
+ 返回:
155
+ LLM 的响应文本
156
+
157
+ 示例:
158
+ # 直接调用
159
+ result = cognitive_call("将这句话翻译成英文:你好世界")
160
+
161
+ # 使用上下文
162
+ with CognitiveContext(api_key="sk-...", model="gpt-4"):
163
+ result = cognitive_call("解释量子纠缠")
164
+ """
165
+ # 获取上下文
166
+ ctx = context or CognitiveContext.get_current()
167
+
168
+ if ctx is None:
169
+ # 无上下文时,创建临时配置
170
+ config = LLMConfig()
171
+ else:
172
+ config = ctx.get_config()
173
+ # 应用覆盖参数
174
+ if model:
175
+ config.model = model
176
+ if temperature is not None:
177
+ config.temperature = temperature
178
+
179
+ # 检查 API 密钥
180
+ if not config.api_key:
181
+ raise ValueError(
182
+ "未配置 API 密钥。请设置 OPENAI_API_KEY 环境变量,"
183
+ "或在 CognitiveContext 中提供 api_key 参数。"
184
+ )
185
+
186
+ # 获取记忆
187
+ memory = ctx.get_memory() if ctx else []
188
+
189
+ # 调用 LLM
190
+ response = _call_openai(config, prompt, memory)
191
+
192
+ # 更新记忆
193
+ if ctx:
194
+ ctx.add_to_memory("user", prompt)
195
+ ctx.add_to_memory("assistant", response)
196
+
197
+ return response