pengent 1.2.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.
Files changed (153) hide show
  1. pengent/__init__.py +0 -0
  2. pengent/agents/__init__.py +9 -0
  3. pengent/agents/agent_base.py +665 -0
  4. pengent/agents/basic/agent_chat.py +42 -0
  5. pengent/agents/basic/agent_coder.py +49 -0
  6. pengent/core/artifacts/artifact.py +66 -0
  7. pengent/core/artifacts/artifact_service/__init__.py +10 -0
  8. pengent/core/artifacts/artifact_service/base_artifact_service.py +166 -0
  9. pengent/core/artifacts/artifact_service/in_memory_artfact_service.py +155 -0
  10. pengent/core/artifacts/artifact_service/storage_artifact_service.py +281 -0
  11. pengent/core/message_memory/__init__.py +4 -0
  12. pengent/core/message_memory/history_message_memory.py +30 -0
  13. pengent/core/message_memory/message_memory.py +80 -0
  14. pengent/core/message_memory/retrieval_message_memory.py +142 -0
  15. pengent/core/message_memory/storage/__init__.py +7 -0
  16. pengent/core/message_memory/storage/file_memory_storage.py +19 -0
  17. pengent/core/message_memory/storage/memory_storage_interface.py +14 -0
  18. pengent/core/message_memory/storage/redis_memory_storage.py +29 -0
  19. pengent/core/message_memory/summary_message_memory.py +42 -0
  20. pengent/core/message_memory/vector/faissy_store.py +124 -0
  21. pengent/core/message_memory/vector/qdrant_store.py +98 -0
  22. pengent/core/message_memory/vector/vectore_store_factory.py +69 -0
  23. pengent/core/message_memory/vector/vectore_store_interface.py +21 -0
  24. pengent/core/runners/__init__.py +5 -0
  25. pengent/core/runners/runner.py +77 -0
  26. pengent/core/sessions/__init__.py +11 -0
  27. pengent/core/sessions/session.py +27 -0
  28. pengent/core/sessions/session_service/database_session_service.py +290 -0
  29. pengent/core/sessions/session_service/in_memory_session_service.py +101 -0
  30. pengent/core/sessions/session_service/redis_session_service.py +165 -0
  31. pengent/core/sessions/session_service/session_service_base.py +105 -0
  32. pengent/core/sessions/session_service/storage_session_service.py +175 -0
  33. pengent/db/base.py +109 -0
  34. pengent/db/models/messages_orm.py +37 -0
  35. pengent/db/models/session_orm.py +25 -0
  36. pengent/errors/already_exists_error.py +14 -0
  37. pengent/lib/__init__.py +10 -0
  38. pengent/lib/common.py +64 -0
  39. pengent/lib/custom_logger.py +130 -0
  40. pengent/lib/singleton.py +24 -0
  41. pengent/llm/__init__.py +14 -0
  42. pengent/llm/batch/llm_batch_anthropic.py +431 -0
  43. pengent/llm/batch/llm_batch_base.py +338 -0
  44. pengent/llm/batch/llm_batch_openai.py +433 -0
  45. pengent/llm/llm_client_anthropic.py +175 -0
  46. pengent/llm/llm_client_base.py +140 -0
  47. pengent/llm/llm_client_factory.py +66 -0
  48. pengent/llm/llm_client_gemini.py +215 -0
  49. pengent/llm/llm_client_gguf.py +140 -0
  50. pengent/llm/llm_client_open_router.py +199 -0
  51. pengent/llm/llm_client_openai.py +180 -0
  52. pengent/policies/__init__.py +19 -0
  53. pengent/policies/actions/__init__.py +7 -0
  54. pengent/policies/actions/action_base.py +71 -0
  55. pengent/policies/polices/all_match_policy.py +25 -0
  56. pengent/policies/polices/best_score_policy.py +32 -0
  57. pengent/policies/polices/first_match_policy.py +17 -0
  58. pengent/policies/polices/policy_base.py +38 -0
  59. pengent/policies/polices/threshold_policy.py +134 -0
  60. pengent/policies/rules/__init__.py +25 -0
  61. pengent/policies/rules/generic_line_rule.py +82 -0
  62. pengent/policies/rules/identifier_rule.py +133 -0
  63. pengent/policies/rules/japanese_intent_rule.py +248 -0
  64. pengent/policies/rules/keyword_ex_rule.py +110 -0
  65. pengent/policies/rules/keyword_rule.py +53 -0
  66. pengent/policies/rules/max_len_rule.py +21 -0
  67. pengent/policies/rules/min_len_rule.py +21 -0
  68. pengent/policies/rules/regex_rule.py +27 -0
  69. pengent/policies/rules/rule_base.py +61 -0
  70. pengent/policies/rules/status_equals_rule.py +17 -0
  71. pengent/policies/rules/value_exists_rule.py +16 -0
  72. pengent/templates/template_factory.py +62 -0
  73. pengent/tools/__init__.py +15 -0
  74. pengent/tools/api/chat/api_rocket_chat.py +164 -0
  75. pengent/tools/api/chat/api_slack.py +79 -0
  76. pengent/tools/api/cloud/api_cloud_base.py +111 -0
  77. pengent/tools/api/cloud/runpod/api_runpod.py +77 -0
  78. pengent/tools/api/git/api_git_base.py +429 -0
  79. pengent/tools/api/git/api_gitea.py +664 -0
  80. pengent/tools/api/git/api_github.py +101 -0
  81. pengent/tools/api/google/api_google_calender.py +246 -0
  82. pengent/tools/api/google/api_google_docs.py +31 -0
  83. pengent/tools/api/google/api_google_drive.py +187 -0
  84. pengent/tools/api/google/api_google_mail.py +46 -0
  85. pengent/tools/api/google/api_google_slides.py +1061 -0
  86. pengent/tools/api/news/api_news_data.py +56 -0
  87. pengent/tools/api/notion/api_notion.py +392 -0
  88. pengent/tools/api/post/api_qiita.py +286 -0
  89. pengent/tools/api/post/api_wiki_js.py +203 -0
  90. pengent/tools/api/project/redmine/__init__.py +5 -0
  91. pengent/tools/api/project/redmine/api_redmine.py +585 -0
  92. pengent/tools/api/report/api_report_pdf.py +182 -0
  93. pengent/tools/api/search/api_brave_search.py +52 -0
  94. pengent/tools/api/search/api_google_web_search.py +57 -0
  95. pengent/tools/api/search/api_read_webpage.py +54 -0
  96. pengent/tools/api/services/api_hotpepper_gourmet.py +36 -0
  97. pengent/tools/api/services/open_weather.py +81 -0
  98. pengent/tools/mcp/__init__.py +3 -0
  99. pengent/tools/mcp/mcp_client.py +70 -0
  100. pengent/tools/mcp/mcp_data.py +130 -0
  101. pengent/tools/mcp/mcp_tool_package.py +166 -0
  102. pengent/tools/tool_utils.py +116 -0
  103. pengent/tools/tools/coder/__init__.py +5 -0
  104. pengent/tools/tools/coder/tool_run_code.py +121 -0
  105. pengent/tools/tools/coder/tool_run_docker_code.py +144 -0
  106. pengent/tools/tools/genaral/files_tool.py +79 -0
  107. pengent/tools/tools/git/tool_giter_basic.py +252 -0
  108. pengent/tools/tools/issues/redmine_tool.py +249 -0
  109. pengent/tools/tools/location/locatoion_tools.py +44 -0
  110. pengent/tools/tools/post/tool_qiita.py +77 -0
  111. pengent/tools/tools/post/tool_wiki_post.py +93 -0
  112. pengent/tools/tools/search/tool_web_search.py +60 -0
  113. pengent/tools/tools/utility/tool_news.py +28 -0
  114. pengent/type/agent/__init__.py +6 -0
  115. pengent/type/agent/agent_enum.py +167 -0
  116. pengent/type/llm/llm_message.py +493 -0
  117. pengent/type/llm/llm_response.py +158 -0
  118. pengent/type/llm/llm_type_enum.py +47 -0
  119. pengent/type/rag.py +103 -0
  120. pengent/type/rag_block.py +84 -0
  121. pengent/type/tool/tool_enum.py +323 -0
  122. pengent/utility/categorizer/__init__.py +0 -0
  123. pengent/utility/categorizer/categorize.py +102 -0
  124. pengent/utility/chunker/block/block_chunker.py +116 -0
  125. pengent/utility/chunker/block/html_parser.py +109 -0
  126. pengent/utility/chunker/block/markdown_parser.py +143 -0
  127. pengent/utility/chunker/block/pdf_parser.py +128 -0
  128. pengent/utility/chunker/chunker_base.py +70 -0
  129. pengent/utility/chunker/chunker_factory.py +77 -0
  130. pengent/utility/chunker/faq/faq_analyzer.py +210 -0
  131. pengent/utility/chunker/faq/pdf_faq_parser.py +89 -0
  132. pengent/utility/chunker/faq/text_faq_parser.py +33 -0
  133. pengent/utility/chunker/text/text_chunker.py +93 -0
  134. pengent/utility/embed/embed_base.py +28 -0
  135. pengent/utility/embed/embed_with_local.py +38 -0
  136. pengent/utility/embed/embed_with_openai.py +35 -0
  137. pengent/utility/reader/html_async_reader.py +96 -0
  138. pengent/utility/reader/html_reader.py +97 -0
  139. pengent/utility/reader/request_async_reader.py +22 -0
  140. pengent/utility/reader/request_reader.py +21 -0
  141. pengent/utility/storage/__init__.py +7 -0
  142. pengent/utility/storage/local_storage.py +130 -0
  143. pengent/utility/storage/storage_base.py +121 -0
  144. pengent/utility/summarize.py +30 -0
  145. pengent/workers/__init__.py +9 -0
  146. pengent/workers/basic/worker_call_tool.py +403 -0
  147. pengent/workers/basic/worker_greet.py +41 -0
  148. pengent/workers/worker_base.py +295 -0
  149. pengent-1.2.1.dist-info/METADATA +52 -0
  150. pengent-1.2.1.dist-info/RECORD +153 -0
  151. pengent-1.2.1.dist-info/WHEEL +5 -0
  152. pengent-1.2.1.dist-info/licenses/LICENSE +9 -0
  153. pengent-1.2.1.dist-info/top_level.txt +1 -0
pengent/__init__.py ADDED
File without changes
@@ -0,0 +1,9 @@
1
+ # basic_agents
2
+ from .agent_base import AgentBase
3
+ from .basic.agent_chat import AgentChat
4
+
5
+
6
+ __all__ = [
7
+ "AgentBase",
8
+ "AgentChat",
9
+ ]
@@ -0,0 +1,665 @@
1
+ import uuid
2
+ import time
3
+ import json
4
+ from datetime import datetime
5
+
6
+ from typing import Union, Optional
7
+ from ..llm.llm_client_base import LLMClientBase
8
+
9
+ # メッセージメモリ及びタスクの状態管理、ツール管理をするためのモジュール
10
+ from ..core.sessions.session import Session
11
+ from ..type.llm.llm_message import LLMMessage, LLMMessageTool
12
+ from ..type.llm.llm_response import LLMResponse
13
+ from ..type.agent.agent_enum import (
14
+ AgentSendInput,
15
+ AgentSendOutput,
16
+ ExecutionContext,
17
+ )
18
+
19
+ from ..tools import ToolUnion, ToolBase, ToolUtils
20
+
21
+
22
+ from ..lib.common import strip_json_fence
23
+
24
+
25
+ class AgentBase:
26
+ """
27
+ 全エージェントの共通基底クラス(personal)
28
+ """
29
+
30
+ def __init__(
31
+ self,
32
+ name: str,
33
+ llm_client: LLMClientBase = None,
34
+ params: dict = None,
35
+ tools: list[ToolUnion] = None,
36
+ logger=None,
37
+ ):
38
+ """
39
+ コンストラクタ
40
+
41
+ Args:
42
+ name (str): エージェント名
43
+ llm_client (LLMClientBase, optional): LLMクライアント
44
+ params (dict, optional): エージェントパラメータ
45
+ tools (list[ToolUnion], optional):ツールリスト.
46
+ logger (optional): ロガーインスタンス.
47
+ """
48
+ self.name = name
49
+ self.llm_client = llm_client
50
+ self.params = params if params else {}
51
+ if logger:
52
+ self.logger = logger
53
+ else:
54
+ from ..lib import get_logger
55
+
56
+ self.logger = get_logger()
57
+
58
+ self.tools: list[ToolUnion] = tools if tools else []
59
+ self._tool_map: dict[str, ToolBase] = {}
60
+
61
+ # コールバックを設定するメソッド
62
+ self.message_callback = None
63
+
64
+ def set_llm_client(self, llm_client: LLMClientBase):
65
+ """LLMクライアントを設定する"""
66
+ self.llm_client = llm_client
67
+
68
+ def add_tool(self, tool: ToolUnion):
69
+ """エージェントにツールを追加するメソッド"""
70
+ if not self.tools:
71
+ self.tools = []
72
+ self.tools.append(tool)
73
+
74
+ def set_system_prompt(self, system_prompt: str):
75
+ """システムプロンプトを設定する(上書き)
76
+
77
+ Args:
78
+ system_prompt (str): システムプロンプト
79
+ """
80
+ self.params["system_prompt"] = system_prompt
81
+
82
+ # ----------------------------
83
+ # Main Method
84
+
85
+ def run(
86
+ self,
87
+ session: Session,
88
+ input: Union[str, dict, AgentSendInput] = None,
89
+ ctx: Optional[ExecutionContext] = None,
90
+ **kwargs,
91
+ ) -> AgentSendOutput:
92
+ """
93
+ エージェントを起動する
94
+ """
95
+ self.logger.info(f"{self.name} is Running.")
96
+
97
+ # LLMクライアントにツールを設定する
98
+ # (_tool_mapがあればキャッシュされているのでスキップ)
99
+ if self.tools and not self._tool_map:
100
+ tool_bases: list[ToolBase] = ToolUtils.normalize_tools(self.tools)
101
+ self._tool_map = {tool.name: tool for tool in tool_bases}
102
+ self.logger.debug("set tools to llm client.")
103
+ self.llm_client.tools = [t.dump() for t in tool_bases]
104
+
105
+ # LLMクライアントにシステムプロンプトを指定する
106
+ self.llm_client.system_prompt = self.system_prompt
107
+
108
+ return self.send(input, session, ctx, **kwargs)
109
+
110
+ def send(
111
+ self,
112
+ input: Union[str, dict, AgentSendInput] = None,
113
+ session: Session = None,
114
+ ctx: Optional[ExecutionContext] = None,
115
+ **kwargs,
116
+ ):
117
+ """
118
+ エージェントにメッセージを送信するメソッド
119
+ """
120
+ try:
121
+ if session is None and ctx is not None:
122
+ raise ValueError("Either session or ctx must be provided.")
123
+ if session is None:
124
+ # セッションが存在しない婆は作成する
125
+ session = Session(
126
+ session_id=uuid.uuid4().hex,
127
+ user_id=f"gestuser-{int(datetime.now().timestamp())}",
128
+ )
129
+
130
+ if not ctx:
131
+ ctx = ExecutionContext.create(session=session)
132
+
133
+ self.logger.debug(f"send message start. input: {input}")
134
+ if input and isinstance(input, dict):
135
+ input = AgentSendInput(**input)
136
+ elif input and isinstance(input, str):
137
+ input = AgentSendInput(content=input)
138
+ input.set_session(session)
139
+
140
+ # ユーザープロンプトの自動生成機能が有効か確認する
141
+ is_make_user_prompt = self.get_value_for_kwargs(
142
+ "is_make_user_prompt", False, **kwargs
143
+ )
144
+
145
+ if is_make_user_prompt:
146
+ self.logger.debug("make user prompt.")
147
+ text = self.make_user_prompt(**input.to_dict())
148
+ else:
149
+ if input.content and isinstance(input.content, str):
150
+ text = input.content.strip()
151
+ elif input.content and isinstance(input.content, list):
152
+ for con in input.content:
153
+ text = con.text.strip()
154
+
155
+ self.logger.debug(f"send message text: {text}")
156
+
157
+ # メッセージの送信処理について
158
+ messages = []
159
+ _message = LLMMessage.create_user_message(text)
160
+ messages.append(_message)
161
+ response = self.llm_client.request(messages=session.events.get() + messages)
162
+ return self.receive_message(session, response, messages, context=ctx)
163
+
164
+ except Exception as e:
165
+ self.logger.exception(f"send message Error: {e}")
166
+ raise e
167
+ finally:
168
+ self.logger.debug("send message end.")
169
+
170
+ def receive_message(
171
+ self,
172
+ session: Session,
173
+ response: LLMResponse,
174
+ messages: list[LLMMessage],
175
+ *,
176
+ context: Optional[ExecutionContext] = None,
177
+ ):
178
+ """
179
+ エージェントが受信したメッセージを処理するメソッド
180
+ """
181
+ # ツールが呼ばれていないか確認する
182
+ tools = response.get_tools()
183
+ if tools:
184
+ response = self.handle_tools_call(session, messages, tools, context=context)
185
+
186
+ # メッセージを確認する
187
+ if not response.is_message():
188
+ # メッセージがない場合は、エラーを返す
189
+ self.logger.error(f"receive_message Error: {response}")
190
+ raise ValueError("no message.")
191
+
192
+ response, output = self._with_retry(session, response, messages)
193
+ _message = LLMMessage.create_assistant_message(output.message)
194
+ messages.append(_message)
195
+
196
+ self.logger.debug(f"receive_message end. {output}")
197
+ return self.handler_message(messages, output, context=context)
198
+
199
+ def _register_tools_call(
200
+ self, messages: list[LLMMessage], tools: list[LLMMessageTool]
201
+ ):
202
+ """Functionsコールをメッセージに登録するメソッド"""
203
+ _message = LLMMessage.create_tools_call(tools)
204
+ messages.append(_message)
205
+
206
+ def _exec_tool_call_response(
207
+ self,
208
+ session: Session,
209
+ result: Union[str, dict, list],
210
+ messages: list[LLMMessage],
211
+ tool: LLMMessageTool,
212
+ ):
213
+ if isinstance(result, list) or isinstance(result, dict):
214
+ result = json.dumps(result)
215
+ _message = LLMMessage.create_tools_result(tool_call_id=tool.id, content=result)
216
+ messages.append(_message) #
217
+ response = self.llm_client.request(messages=session.events.get() + messages)
218
+ return response
219
+
220
+ def _exec_tool_call(
221
+ self,
222
+ session: Session,
223
+ messages: list[LLMMessage],
224
+ tool: LLMMessageTool,
225
+ *,
226
+ context: Optional[ExecutionContext] = None,
227
+ ):
228
+ """
229
+ ツールコールを実行するメソッド
230
+ """
231
+ self.logger.debug(f"tool: {tool.function.name} {tool.function.arguments}")
232
+
233
+ # ツールを実行するためのMapを持つ
234
+ name = tool.function.name
235
+ tool_base = self._tool_map.get(name)
236
+ if not tool_base:
237
+ result = json.dumps({"error": f"Tool Not Found in Agent: {name}"})
238
+ else:
239
+ result = ToolUtils.execute_tool(
240
+ tool_base,
241
+ tool.function.arguments,
242
+ context=context,
243
+ )
244
+ return self._exec_tool_call_response(session, result, messages, tool)
245
+
246
+ def handle_tools_call(
247
+ self,
248
+ session: Session,
249
+ messages: list[LLMMessage],
250
+ tools: list[LLMMessageTool],
251
+ *,
252
+ context: Optional[ExecutionContext] = None,
253
+ ):
254
+ self.logger.debug(f"handle_tools_call receive tools: {tools}")
255
+ self._register_tools_call(messages, tools)
256
+ for tool in tools:
257
+ response = self._exec_tool_call(session, messages, tool, context=context)
258
+
259
+ return response
260
+
261
+ def handler_message(
262
+ self,
263
+ messages: list[LLMMessage],
264
+ output: AgentSendOutput,
265
+ *,
266
+ context: Optional[ExecutionContext] = None,
267
+ ) -> AgentSendOutput:
268
+ """
269
+ エージェントからの応答を処理するメソッド
270
+ """
271
+ output.events_messages = messages
272
+ self._output = output
273
+
274
+ if context.get_state_delta():
275
+ output.set_context("state_delta", context.get_state_delta())
276
+
277
+ if self.message_callback:
278
+ # メッセージコールバックが設定されている場合は呼び出す
279
+ self.logger.debug("call message callback.")
280
+ self.message_callback(messages, output)
281
+
282
+ return output
283
+
284
+ def _with_retry(
285
+ self,
286
+ session: Session,
287
+ response: LLMResponse,
288
+ messages: list[LLMMessage],
289
+ ) -> tuple[LLMResponse, AgentSendOutput]:
290
+ """LLMメッセージリクエストをパース(リトライ/再送信)するための中間メソッド"""
291
+ # リトライの回数とリトライ間隔
292
+ max_retries = self.params.get("retry_max_count", 3)
293
+ retry_delay_sec = self.params.get("retry_delay_sec", 10)
294
+
295
+ for attempt in range(max_retries):
296
+ try:
297
+ output = self._parse_response_content(response)
298
+ return response, output # 成功
299
+ except Exception as e:
300
+ error_detail = str(e)
301
+ self.logger.error(
302
+ f"Parse failed on attempt {attempt + 1}: {error_detail}"
303
+ )
304
+ messages.append(
305
+ LLMMessage.create_assistant_message(response.get_message())
306
+ )
307
+ messages.append(
308
+ LLMMessage.create_user_message(
309
+ content=(
310
+ "**Failed to parse the response.**\n"
311
+ "Please make sure the output format is correct "
312
+ "and includes all required keys.\n"
313
+ f"(Detail: {error_detail})"
314
+ )
315
+ )
316
+ )
317
+ if attempt < max_retries - 1:
318
+ time.sleep(retry_delay_sec)
319
+ response = self.llm_client.request(
320
+ messages=session.events.get() + messages
321
+ )
322
+ else:
323
+ raise ValueError(
324
+ f"Parsing failed after {max_retries} attempts. "
325
+ f"Last error: {error_detail}"
326
+ ) from e
327
+
328
+ def _parse_response_content(self, response: LLMResponse) -> AgentSendOutput:
329
+ """パースを実行するメソッド"""
330
+ self.logger.debug("_parse_response_content start.")
331
+ self.logger.debug(
332
+ f"format: {self.format} is_contents_type: {self.is_contents_type}"
333
+ )
334
+ content_text = response.get_message()
335
+ if self.is_contents_type:
336
+ # 先頭に出力タイプ
337
+ lines = content_text.splitlines()
338
+ self.logger.info(
339
+ f"contents_type(Head 1 Line):{lines[0].strip() if lines else ''}"
340
+ )
341
+ content_text = "\n".join(lines[1:]) if len(lines) > 1 else ""
342
+
343
+ self.logger.debug(f"content_text:\n{content_text}")
344
+
345
+ if self.params.get("callback_parse"): # 独自のパース関数で実行する場合
346
+ self.logger.debug("Using custom call_parse method.")
347
+ return self.params["callback_parse"](content_text)
348
+
349
+ # 通常パースを実行する
350
+ if self.format == "application/json":
351
+ self.logger.debug("Parsing as JSON.")
352
+ content_text = strip_json_fence(content_text)
353
+ content_json = self.llm_client.parse_json(content_text)
354
+ output = AgentSendOutput()
355
+ output.message = content_json.get("message", "JSON Response No Message")
356
+ output.set_context("json", content_json)
357
+ return output
358
+ else:
359
+ self.logger.debug("no parse text.")
360
+ return AgentSendOutput(message=content_text)
361
+
362
+ # ----------------------------
363
+ # params
364
+
365
+ @property
366
+ def format(self) -> str:
367
+ return self.params.get("format", "text/plain")
368
+
369
+ @format.setter
370
+ def format(self, value: str):
371
+ self.params["format"] = value
372
+
373
+ @property
374
+ def call_agents(self) -> list[dict]:
375
+ """呼び出せる関数・ツールのリスト"""
376
+ return self.params.get("call_agents", None)
377
+
378
+ @call_agents.setter
379
+ def call_agents(self, value: list[dict]):
380
+ self.params["call_agents"] = value
381
+
382
+ def dump_call_agents(self):
383
+ """呼び出すことのできるエージェントJSON形式でダンプするメソッド(プロンプト用)"""
384
+ return json.dumps(self.call_agents, indent=2, ensure_ascii=False)
385
+
386
+ @property
387
+ def system_prompt(self) -> str:
388
+ """システムプロンプトを取得する
389
+
390
+ Notes:
391
+ - paramsにsystem_promptが設定されていれば、優先して使用する。
392
+ - それ以外の場合は、make_system_promptメソッドを使用して生成する。
393
+ """
394
+ if self.params.get("system_prompt", None):
395
+ return self.params["system_prompt"]
396
+ else:
397
+ return self.make_system_prompt()
398
+
399
+ def update_param(self, key: str, value: any):
400
+ """
401
+ エージェントのパラメータを更新するメソッド
402
+
403
+ Parameters:
404
+ key (str): パラメータのキー
405
+ value (any): パラメータの値
406
+ """
407
+ self.params[key] = value
408
+
409
+ def clear_param(self, key: str):
410
+ """
411
+ エージェントのパラメータを更新するメソッド
412
+
413
+ Parameters:
414
+ key (str): パラメータのキー
415
+ """
416
+ if key in self.params:
417
+ del self.params[key]
418
+
419
+ def get_value_for_session_stat(
420
+ self, session: Session, key, default=None, **kwargs
421
+ ) -> any:
422
+ val = session.stat.get(key, None)
423
+ if val:
424
+ return val
425
+ return kwargs.get(key, self.params.get(key, default))
426
+
427
+ def get_value_for_kwargs(self, key, default=None, **kwargs) -> any:
428
+ return kwargs.get(key, self.params.get(key, default))
429
+
430
+ def get_value_for_params(
431
+ self,
432
+ key,
433
+ default=None,
434
+ ) -> any:
435
+ return self.params.get(key, default)
436
+
437
+ # -----------------------------
438
+ # Agentのオプション設定項目
439
+
440
+ @property
441
+ def is_disable_system_pronpt(self) -> bool:
442
+ """システムプロンプトを禁止する"""
443
+ return self.params.get("is_disable_system_pronpt", False)
444
+
445
+ @is_disable_system_pronpt.setter
446
+ def is_disable_system_pronpt(self, value: bool):
447
+ self.params["is_disable_system_pronpt"] = value
448
+
449
+ @property
450
+ def is_contents_type(self) -> bool:
451
+ """受信メッセージの先頭(1行目)に出力タイプを含むかどうか"""
452
+ return self.params.get("is_contents_type", False)
453
+
454
+ @is_contents_type.setter
455
+ def is_contents_type(self, value: bool):
456
+ self.params["is_contents_type"] = value
457
+
458
+ @property
459
+ def is_make_user_prompt(self) -> bool:
460
+ """ユーザープロンプトを自動生成するかどうか"""
461
+ return self.params.get("is_make_user_prompt", False)
462
+
463
+ @is_make_user_prompt.setter
464
+ def is_make_user_prompt(self, value: bool):
465
+ self.params["is_make_user_prompt"] = value
466
+
467
+ @property
468
+ def is_outoput_example(self) -> bool:
469
+ """システムプロンプトに出力のサンプルを自動的に含めるかどうか
470
+ Notes:
471
+ - 手動でexampleを設定した場合は、is_outoput_exampleは無効となる。
472
+ """
473
+ return self.params.get("is_outoput_example", True)
474
+
475
+ @is_outoput_example.setter
476
+ def is_outoput_example(self, value: bool):
477
+ self.params["is_outoput_example"] = value
478
+
479
+ @property
480
+ def is_outoput_schema(self) -> bool:
481
+ """
482
+ システムプロンプトに出力するJSONスキーマを含むかどうか(JSON形式)
483
+
484
+ Notes:
485
+ - 手動でschemaを設定した場合は、is_outoput_schemaは無効となる。
486
+ - トークンが増えるためデフォルトではFalseに設定されている。
487
+ """
488
+ return self.params.get("is_outoput_schema", False)
489
+
490
+ @is_outoput_schema.setter
491
+ def is_outoput_schema(self, value: bool):
492
+ self.params["is_outoput_schema"] = value
493
+
494
+ @property
495
+ def is_add_outoput_constraints(self) -> bool:
496
+ """システムプロンプトに出力フォーマットにあわせた制約を自動的に含めるかどうか"""
497
+ return self.params.get("is_add_outoput_constraints", True)
498
+
499
+ @is_add_outoput_constraints.setter
500
+ def is_add_outoput_constraints(self, value: bool):
501
+ self.params["is_add_outoput_constraints"] = value
502
+
503
+ # ----------------------------
504
+ # system_prompt
505
+ def make_system_prompt(self) -> str:
506
+ """
507
+ システムプロンプトを生成するメソッド
508
+ """
509
+ if self.is_disable_system_pronpt:
510
+ return None
511
+
512
+ system_prompt = ""
513
+
514
+ role = self.get_value_for_params("role", "you are an assistant")
515
+ system_prompt += f"## Role:\n{role}\n\n"
516
+
517
+ goal = self.get_value_for_params("goal")
518
+ if goal:
519
+ system_prompt += "## Goal\nあなたの役割は次の通りです。\n"
520
+ system_prompt += f"目的: {goal}\n\n"
521
+
522
+ # 出力フォーマット
523
+ system_prompt += "## Output\n\n"
524
+
525
+ if self.format == "text/plain":
526
+ system_prompt += (
527
+ "期待される出力は自然な日本語の文章です。構造化は不要です。\n\n"
528
+ )
529
+ elif self.format == "application/json":
530
+ system_prompt += "期待される出力は純粋な JSON オブジェクトです。\n"
531
+ system_prompt += "コードブロック(``` など)は出力しないこと\n\n"
532
+ elif self.format == "text/markdown":
533
+ system_prompt += "期待される出力はMarkdown形式の構造化された文章です。\n"
534
+ system_prompt += (
535
+ "見出し(#, ##, ###など)やリスト、表、"
536
+ "コードブロックなどを適切に使用すること。\n"
537
+ )
538
+ system_prompt += (
539
+ "読みやすさと構造化を意識し、自然な日本語で記述すること。\n\n"
540
+ )
541
+
542
+ # 出力スキーマを出力する
543
+ schema = self.get_value_for_params("schema", None)
544
+ if schema:
545
+ system_prompt += f"### Schema:\n````\n{schema}\n````\n\n"
546
+
547
+ # 出力サンプルを出力する
548
+ example = self.get_value_for_params("example", None)
549
+ if example:
550
+ system_prompt += "### Example:n"
551
+ if self.is_contents_type:
552
+ system_prompt += f"{self.format}\n"
553
+ system_prompt += f"{example}\n\n"
554
+
555
+ # 呼び出せるツール・関数を制御する
556
+ if self.call_agents:
557
+ system_prompt += "## Enable Calling Agents\n"
558
+ system_prompt += "必要な時は次のエージェントを呼び出すことができます:\n"
559
+ system_prompt += f"````\n{self.dump_call_agents()}\n````\n\n"
560
+
561
+ # SOPに関する指示
562
+ sop_list = []
563
+ sop_list += self.get_value_for_params("sop", [])
564
+ if sop_list:
565
+ system_prompt += "## SOP (ordered)\n"
566
+ system_prompt += "\n".join(f"- {s}" for s in sop_list) + "\n\n"
567
+
568
+ # 実行ルールを明記(順序厳守/ブロック時の振る舞い)
569
+ system_prompt += (
570
+ "### 実行ルール\n"
571
+ "- 上から順に1→2 → ... と実行すること"
572
+ "(指示のない順番変更・スキップ・並行実行は禁止)。\n"
573
+ "- 実行不能な場合は 理由 または不足情報(最大3つ)のみ返し、"
574
+ "推測で進めないこと。\n"
575
+ "- 上記に従わない出力は無効とみなし破棄する。\n\n"
576
+ )
577
+
578
+ # 制約に関する指示
579
+ constraints = []
580
+ constraints += self.get_value_for_params("constraints", [])
581
+ if isinstance(constraints, str):
582
+ constraints = [constraints]
583
+
584
+ if constraints:
585
+ constraints += [
586
+ "**上記の指示を守れない場合は、出力は破棄され、再実行されます。**"
587
+ ]
588
+ system_prompt += "## Constraints:\n"
589
+ system_prompt += (
590
+ f"{chr(10).join([f'- {constraint}' for constraint in constraints])}\n\n"
591
+ )
592
+
593
+ return system_prompt
594
+
595
+ # ----------------------------
596
+ # user_prompt
597
+
598
+ def make_user_prompt(self, **kwargs) -> str:
599
+ user_prompt = ""
600
+ content = kwargs.get("content", None)
601
+ # 本文を出力する
602
+ if content:
603
+ if isinstance(content, str):
604
+ _content = content.strip()
605
+ elif isinstance(content, list):
606
+ for con in content:
607
+ _content = con.text.strip()
608
+ user_prompt += f"{_content}"
609
+
610
+ state = self.get_value_for_kwargs("state", None, **kwargs)
611
+ summary = self.get_value_for_kwargs("summary", None, **kwargs)
612
+ prior_info = self.get_value_for_kwargs("prior_info", None, **kwargs)
613
+ template: str = self.get_value_for_kwargs("template", None, **kwargs)
614
+
615
+ if not state and not summary and not prior_info and not template:
616
+ return user_prompt
617
+
618
+ if content:
619
+ user_prompt += "\n\n---\n"
620
+
621
+ if state:
622
+ user_prompt += f"## State:\n```\n{json.dumps(state, indent=2)}\n```\n\n"
623
+
624
+ if summary:
625
+ user_prompt += f"## Summary:\n{summary}\n\n"
626
+
627
+ if prior_info:
628
+ lines = [
629
+ f"- {key}:{chr(10).join(f'・{v}' for v in value)}"
630
+ if isinstance(value, list)
631
+ else f"{key}:{value}"
632
+ for key, value in prior_info.items()
633
+ ]
634
+ prior = "\n".join(lines)
635
+ user_prompt += f"## Prior Info:\n{prior}\n\n"
636
+
637
+ if template:
638
+ if template.startswith("file://"):
639
+ template_file = template.replace("file://", "")
640
+ with open(template_file, "r", encoding="utf-8") as f:
641
+ _template = f.read()
642
+ else:
643
+ _template = template
644
+
645
+ user_prompt += f"## Template:\n```\n{_template}\n```\n\n"
646
+
647
+ return user_prompt
648
+
649
+ def to_dict(self) -> dict:
650
+ """
651
+ エージェント情報を辞書形式に変換します。
652
+ """
653
+ data = {
654
+ "class": self.__class__.__name__,
655
+ "name": self.name,
656
+ "params": self.params,
657
+ # "tools": self.tools,
658
+ "call_agents": self.call_agents,
659
+ "system_prompt": self.system_prompt,
660
+ "llm_model_name": self.llm_client.model_name if self.llm_client else None,
661
+ "llm_temperature": self.llm_client.temperature if self.llm_client else None,
662
+ "llm_llm_type": self.llm_client.llm_type if self.llm_client else None,
663
+ "llm_config": self.llm_client.config if self.llm_client else None,
664
+ }
665
+ return data