agently 4.0.7.2__py3-none-any.whl → 4.0.7.4__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.
@@ -23,9 +23,9 @@ from typing import Any, Sequence, TYPE_CHECKING
23
23
  from agently.core import BaseAgent
24
24
  from agently.core.Session import Session
25
25
  from agently.utils import DataPathBuilder, FunctionShifter
26
+ from agently.types.data import ChatMessage
26
27
 
27
28
  if TYPE_CHECKING:
28
- from agently.types.data import ChatMessage
29
29
  from agently.core import Prompt
30
30
  from agently.core.ModelRequest import ModelResponseResult
31
31
  from agently.utils import Settings
@@ -123,9 +123,15 @@ class SessionExtension(BaseAgent):
123
123
  def _normalize_chat_history(
124
124
  self, chat_history: Sequence[dict[str, Any] | ChatMessage] | dict[str, Any] | ChatMessage
125
125
  ):
126
- if not isinstance(chat_history, list):
127
- return [chat_history]
128
- return chat_history
126
+ messages: list[ChatMessage] = []
127
+ if not isinstance(chat_history, Sequence):
128
+ chat_history = [chat_history]
129
+ for message in chat_history:
130
+ if not isinstance(message, ChatMessage):
131
+ messages.append(ChatMessage(role=message["role"], content=message["content"]))
132
+ else:
133
+ messages.append(message)
134
+ return messages
129
135
 
130
136
  def _stringify_content(self, content: Any):
131
137
  if content is None:
agently/core/Session.py CHANGED
@@ -22,25 +22,43 @@ if TYPE_CHECKING:
22
22
  from agently.core import PluginManager, BaseAgent
23
23
  from agently.types.plugins import (
24
24
  SessionProtocol,
25
+ SessionMode,
26
+ SessionLimit,
27
+ MemoResizeDecision,
28
+ MemoResizeType,
25
29
  MemoResizePolicyHandler,
26
30
  MemoResizeHandler,
27
31
  AttachmentSummaryHandler,
28
32
  MemoUpdateHandler,
29
33
  )
34
+ from agently.types.data import ChatMessage, ChatMessageDict, SerializableValue, SerializableData
30
35
 
31
36
 
32
37
  class Session:
38
+ _impl: "SessionProtocol"
39
+ settings: Settings
40
+ plugin_manager: "PluginManager"
41
+ id: str
42
+ memo: "SerializableData"
43
+ full_chat_history: "list[ChatMessage]"
44
+ current_chat_history: "list[ChatMessage]"
45
+
33
46
  def __init__(
34
47
  self,
35
48
  *,
36
49
  policy_handler: "MemoResizePolicyHandler | None" = None,
37
- resize_handlers: "dict[Literal['lite', 'deep'] | str, MemoResizeHandler] | None" = None,
50
+ resize_handlers: "dict[MemoResizeType, MemoResizeHandler] | None" = None,
38
51
  attachment_summary_handler: "AttachmentSummaryHandler | None" = None,
39
52
  memo_update_handler: "MemoUpdateHandler | None" = None,
40
53
  parent_settings: Settings | None = None,
41
54
  agent: "BaseAgent | None" = None,
42
55
  plugin_manager: "PluginManager | None" = None,
43
56
  ):
57
+ if agent is not None:
58
+ if plugin_manager is None and hasattr(agent, "plugin_manager"):
59
+ plugin_manager = agent.plugin_manager
60
+ if parent_settings is None and hasattr(agent, "settings"):
61
+ parent_settings = agent.settings
44
62
  if plugin_manager is None:
45
63
  from agently.base import plugin_manager as global_plugin_manager, settings as global_settings
46
64
 
@@ -72,6 +90,126 @@ class Session:
72
90
  object.__setattr__(self, "settings", impl.settings)
73
91
  object.__setattr__(self, "plugin_manager", plugin_manager)
74
92
 
93
+ def configure(
94
+ self,
95
+ *,
96
+ mode: "SessionMode | None" = None,
97
+ limit: "SessionLimit | None" = None,
98
+ every_n_turns: int | None = None,
99
+ ) -> "Session":
100
+ self._impl.configure(
101
+ mode=mode,
102
+ limit=limit,
103
+ every_n_turns=every_n_turns,
104
+ )
105
+ return self
106
+
107
+ def set_limit(
108
+ self,
109
+ *,
110
+ chars: int | None = None,
111
+ messages: int | None = None,
112
+ ) -> "Session":
113
+ self._impl.set_limit(chars=chars, messages=messages)
114
+ return self
115
+
116
+ def use_lite(
117
+ self,
118
+ *,
119
+ chars: int | None = None,
120
+ messages: int | None = None,
121
+ every_n_turns: int | None = None,
122
+ ) -> "Session":
123
+ self._impl.use_lite(chars=chars, messages=messages, every_n_turns=every_n_turns)
124
+ return self
125
+
126
+ def use_memo(
127
+ self,
128
+ *,
129
+ chars: int | None = None,
130
+ messages: int | None = None,
131
+ every_n_turns: int | None = None,
132
+ ) -> "Session":
133
+ self._impl.use_memo(chars=chars, messages=messages, every_n_turns=every_n_turns)
134
+ return self
135
+
136
+ def append_message(self, message: "ChatMessage | ChatMessageDict") -> "Session":
137
+ self._impl.append_message(message)
138
+ return self
139
+
140
+ def set_settings(
141
+ self,
142
+ key: str,
143
+ value: "SerializableValue",
144
+ *,
145
+ auto_load_env: bool = False,
146
+ ) -> Settings:
147
+ return self._impl.set_settings(key, value, auto_load_env=auto_load_env)
148
+
149
+ def set_policy_handler(self, policy_handler: "MemoResizePolicyHandler") -> "Session":
150
+ self._impl.set_policy_handler(policy_handler)
151
+ return self
152
+
153
+ def set_resize_handlers(
154
+ self,
155
+ resize_type: "MemoResizeType",
156
+ resize_handler: "MemoResizeHandler",
157
+ ) -> "Session":
158
+ self._impl.set_resize_handlers(resize_type, resize_handler)
159
+ return self
160
+
161
+ def set_attachment_summary_handler(
162
+ self,
163
+ attachment_summary_handler: "AttachmentSummaryHandler",
164
+ ) -> "Session":
165
+ self._impl.set_attachment_summary_handler(attachment_summary_handler)
166
+ return self
167
+
168
+ def set_memo_update_handler(
169
+ self,
170
+ memo_update_handler: "MemoUpdateHandler",
171
+ ) -> "Session":
172
+ self._impl.set_memo_update_handler(memo_update_handler)
173
+ return self
174
+
175
+ def judge_resize(
176
+ self,
177
+ force: "Literal['lite', 'deep', False, None] | str" = False,
178
+ ) -> "MemoResizeDecision | None":
179
+ return self._impl.judge_resize(force=force)
180
+
181
+ def resize(
182
+ self,
183
+ force: "Literal['lite', 'deep', False, None] | str" = False,
184
+ ) -> "list[ChatMessage]":
185
+ return self._impl.resize(force=force)
186
+
187
+ async def async_judge_resize(
188
+ self,
189
+ force: "Literal['lite', 'deep', False, None] | str" = False,
190
+ ) -> "MemoResizeDecision | None":
191
+ return await self._impl.async_judge_resize(force=force)
192
+
193
+ async def async_resize(
194
+ self,
195
+ force: "Literal['lite', 'deep', False, None] | str" = False,
196
+ ) -> "list[ChatMessage]":
197
+ return await self._impl.async_resize(force=force)
198
+
199
+ def to_json(self) -> str:
200
+ return self._impl.to_json()
201
+
202
+ def to_yaml(self) -> str:
203
+ return self._impl.to_yaml()
204
+
205
+ def load_json(self, value: str) -> "Session":
206
+ self._impl.load_json(value)
207
+ return self
208
+
209
+ def load_yaml(self, value: str) -> "Session":
210
+ self._impl.load_yaml(value)
211
+ return self
212
+
75
213
  def __getattr__(self, name: str):
76
214
  return getattr(self._impl, name)
77
215
 
agently/core/__init__.py CHANGED
@@ -25,6 +25,7 @@ from .TriggerFlow import (
25
25
  TriggerFlowExecution,
26
26
  TriggerFlowChunk,
27
27
  )
28
+ from .Session import Session
28
29
 
29
30
  # from .TriggerFlow_old import (
30
31
  # TriggerFlow,
@@ -0,0 +1,18 @@
1
+ # Copyright 2023-2025 AgentEra(Agently.Tech)
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+
16
+ from agently.utils import LazyImport
17
+
18
+ LazyImport.import_package("fastapi")
@@ -20,10 +20,11 @@ from typing_extensions import TypedDict, NotRequired, Self
20
20
 
21
21
  if TYPE_CHECKING:
22
22
  from agently.utils import Settings
23
- from agently.types.data import SerializableData, ChatMessage
23
+ from agently.types.data import SerializableData, SerializableValue, ChatMessage, ChatMessageDict
24
24
 
25
25
  MemoResizeType: TypeAlias = Literal["lite", "deep"] | str
26
26
  SessionMode: TypeAlias = Literal["lite", "memo"] | str
27
+ ResizeForce: TypeAlias = Literal["lite", "deep", False, None] | str
27
28
 
28
29
 
29
30
  class SessionLimit(TypedDict, total=False):
@@ -86,15 +87,12 @@ class SessionProtocol(Protocol):
86
87
  memo: "SerializableData"
87
88
  full_chat_history: "list[ChatMessage]"
88
89
  current_chat_history: "list[ChatMessage]"
89
- set_settings: Callable[..., Any]
90
- judge_resize: Callable[..., Any]
91
- resize: Callable[..., Any]
92
90
 
93
91
  def __init__(
94
92
  self,
95
93
  *,
96
94
  policy_handler: MemoResizePolicyHandler | None = None,
97
- resize_handlers: dict[Literal["lite", "deep"] | str, MemoResizeHandler] | None = None,
95
+ resize_handlers: dict[MemoResizeType, MemoResizeHandler] | None = None,
98
96
  attachment_summary_handler: AttachmentSummaryHandler | None = None,
99
97
  memo_update_handler: MemoUpdateHandler | None = None,
100
98
  parent_settings: "Settings | None" = None,
@@ -132,13 +130,25 @@ class SessionProtocol(Protocol):
132
130
  every_n_turns: int | None = None,
133
131
  ) -> Self: ...
134
132
 
135
- def append_message(self, message: "ChatMessage | dict[str, Any]") -> Self: ...
133
+ def append_message(self, message: "ChatMessage | ChatMessageDict") -> Self: ...
134
+
135
+ def set_settings(
136
+ self,
137
+ key: str,
138
+ value: "SerializableValue",
139
+ *,
140
+ auto_load_env: bool = False,
141
+ ) -> "Settings": ...
142
+
143
+ def judge_resize(self, force: ResizeForce = False) -> "MemoResizeDecision | None": ...
144
+
145
+ def resize(self, force: ResizeForce = False) -> "list[ChatMessage]": ...
136
146
 
137
147
  def set_policy_handler(self, policy_handler: MemoResizePolicyHandler) -> Self: ...
138
148
 
139
149
  def set_resize_handlers(
140
150
  self,
141
- resize_type: Literal["lite", "deep"] | str,
151
+ resize_type: MemoResizeType,
142
152
  resize_handler: MemoResizeHandler,
143
153
  ) -> Self: ...
144
154
 
@@ -146,9 +156,9 @@ class SessionProtocol(Protocol):
146
156
 
147
157
  def set_memo_update_handler(self, memo_update_handler: MemoUpdateHandler) -> Self: ...
148
158
 
149
- async def async_judge_resize(self, force: Literal["lite", "deep", False, None] | str = False): ...
159
+ async def async_judge_resize(self, force: ResizeForce = False) -> "MemoResizeDecision | None": ...
150
160
 
151
- async def async_resize(self, force: Literal["lite", "deep", False, None] | str = False): ...
161
+ async def async_resize(self, force: ResizeForce = False) -> "list[ChatMessage]": ...
152
162
 
153
163
  def to_json(self) -> str: ...
154
164
 
@@ -0,0 +1,351 @@
1
+ Metadata-Version: 2.4
2
+ Name: agently
3
+ Version: 4.0.7.4
4
+ Summary:
5
+ License: Apache-2.0
6
+ License-File: LICENSE
7
+ Author: Agently Team
8
+ Author-email: developer@agently.tech
9
+ Requires-Python: >=3.10
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Requires-Dist: greenlet (>=3.2.3,<4.0.0)
18
+ Requires-Dist: httpx (>=0.28.1,<0.29.0)
19
+ Requires-Dist: httpx-sse (>=0.4.1,<0.5.0)
20
+ Requires-Dist: json5 (>=0.12.0,<0.13.0)
21
+ Requires-Dist: packaging (>=25.0,<26.0)
22
+ Requires-Dist: pydantic (>=2.11.7,<3.0.0)
23
+ Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
24
+ Requires-Dist: stamina (>=25.1.0,<26.0.0)
25
+ Requires-Dist: toml (>=0.10.2,<0.11.0)
26
+ Description-Content-Type: text/markdown
27
+
28
+ <img width="640" alt="image" src="https://github.com/user-attachments/assets/c645d031-c8b0-4dba-a515-9d7a4b0a6881" />
29
+
30
+ # Agently 4 🚀
31
+
32
+ > **Build production‑grade AI apps faster, with stable outputs and maintainable workflows.**
33
+
34
+ [English Introduction](https://github.com/AgentEra/Agently/blob/main/README.md) | [中文介绍](https://github.com/AgentEra/Agently/blob/main/README_CN.md)
35
+
36
+ [![license](https://img.shields.io/badge/license-Apache%202.0-blue.svg)](https://github.com/AgentEra/Agently/blob/main/LICENSE)
37
+ [![PyPI version](https://img.shields.io/pypi/v/agently.svg)](https://pypi.org/project/agently/)
38
+ [![Downloads](https://img.shields.io/pypi/dm/agently.svg)](https://pypistats.org/packages/agently)
39
+ [![GitHub Stars](https://img.shields.io/github/stars/AgentEra/Agently.svg?style=social)](https://github.com/AgentEra/Agently/stargazers)
40
+ [![Twitter Follow](https://img.shields.io/twitter/follow/AgentlyTech?style=social)](https://x.com/AgentlyTech)
41
+ <a href="https://doc.weixin.qq.com/forms/AIoA8gcHAFMAScAhgZQABIlW6tV3l7QQf">
42
+ <img alt="WeChat" src="https://img.shields.io/badge/WeChat%20Group-Join-brightgreen?logo=wechat&style=flat-square">
43
+ </a>
44
+
45
+ <p align="center">
46
+ <a href="https://github.com/AgentEra/Agently/discussions"><img src="https://img.shields.io/badge/💬_Community-Join-blueviolet?style=for-the-badge"></a>
47
+ <a href="https://agently.tech"><img src="https://img.shields.io/badge/🌐_Website-Docs-brightgreen?style=for-the-badge"></a>
48
+ <a href="https://github.com/AgentEra/Agently/issues"><img src="https://img.shields.io/badge/🐛_Issues-Report-red?style=for-the-badge"></a>
49
+ </p>
50
+
51
+ ---
52
+
53
+ <p align="center">
54
+ <b>🔥 <a href="https://agently.tech/docs">Latest Docs</a> | 🚀 <a href="#quickstart">5‑minute Quickstart</a> | 💡 <a href="#-core-features">Core Features</a></b>
55
+ </p>
56
+
57
+ ---
58
+
59
+ ## 📚 Quick Links
60
+
61
+ - **Docs (EN)**: https://agently.tech/docs
62
+ - **Docs (中文)**: https://agently.cn/docs
63
+ - **Agent Systems Playbook (EN)**: https://agently.tech/docs/en/agent-systems/overview.html
64
+ - **Agent Systems Playbook (中文)**: https://agently.cn/docs/agent-systems/overview.html
65
+ - **Coding Agent Guide (EN)**: https://agently.tech/docs/en/agent-docs.html
66
+ - **Coding Agent Guide (中文)**: https://agently.cn/docs/agent-docs.html
67
+ - **Agent Docs Pack**: https://agently.cn/docs/agent_docs.zip
68
+
69
+ ## 🤔 Why Agently?
70
+
71
+ Many GenAI POCs fail in production not because models are weak, but because **engineering control is missing**:
72
+
73
+ | Common challenge | How Agently helps |
74
+ |:--|:--|
75
+ | Output schema drifts, JSON parsing fails | **Contract‑first output control** with `output()` + `ensure_keys` |
76
+ | Workflows get complex and hard to maintain | **TriggerFlow orchestration** with `to` / `if` / `match` / `batch` / `for_each` |
77
+ | Multi‑turn state becomes unstable | **Session & Memo** with memory, summaries, and persistence strategies |
78
+ | Tool calls are hard to audit | **Tool logs** via `extra.tool_logs` |
79
+ | Switching models is expensive | **OpenAICompatible** unified model settings |
80
+
81
+ **Agently turns LLM uncertainty into a stable, testable, maintainable engineering system.**
82
+
83
+ ## ✨ Core Features
84
+
85
+ ### 1) 📝 Contract‑first Output Control
86
+ Define the structure with `output()`, enforce critical keys with `ensure_keys`.
87
+
88
+ ```python
89
+ result = (
90
+ agent
91
+ .input("Analyze user feedback")
92
+ .output({
93
+ "sentiment": (str, "positive/neutral/negative"),
94
+ "key_issues": [(str, "issue summary")],
95
+ "priority": (int, "1-5, 5 is highest")
96
+ })
97
+ .start(ensure_keys=["sentiment", "key_issues[*]"])
98
+ )
99
+ ```
100
+
101
+ ### 2) ⚡ Structured Streaming (Instant)
102
+ Consume structured fields as they are generated.
103
+
104
+ ```python
105
+ response = (
106
+ agent
107
+ .input("Explain recursion and give 2 tips")
108
+ .output({"definition": (str, "one sentence"), "tips": [(str, "tip")]})
109
+ .get_response()
110
+ )
111
+
112
+ for msg in response.get_generator(type="instant"):
113
+ if msg.path == "definition" and msg.delta:
114
+ ui.update_definition(msg.delta)
115
+ if msg.wildcard_path == "tips[*]" and msg.delta:
116
+ ui.add_tip(msg.delta)
117
+ ```
118
+
119
+ ### 3) 🧩 TriggerFlow Orchestration
120
+ Readable, testable workflows with branching and concurrency.
121
+
122
+ ```python
123
+ (
124
+ flow.to(handle_request)
125
+ .if_condition(lambda d: d.value["type"] == "query")
126
+ .to(handle_query)
127
+ .elif_condition(lambda d: d.value["type"] == "order")
128
+ .to(check_inventory)
129
+ .to(create_order)
130
+ .end_condition()
131
+ )
132
+ ```
133
+
134
+ ### 4) 🧠 Session & Memo (Multi‑turn Memory)
135
+ Quick / Lite / Memo modes with summaries and persistence strategies.
136
+
137
+ ```python
138
+ from agently import Agently
139
+ from agently.core import Session
140
+
141
+ agent = Agently.create_agent()
142
+ session = Session(agent=agent).configure(
143
+ mode="memo",
144
+ limit={"chars": 6000, "messages": 12},
145
+ every_n_turns=2,
146
+ )
147
+ agent.attach_session(session)
148
+ ```
149
+
150
+ ### 5) 🔧 Tool Calls + Logs
151
+ Tool selection and usage are logged in `extra.tool_logs`.
152
+
153
+ ```python
154
+ @agent.tool_func
155
+ def add(a: int, b: int) -> int:
156
+ return a + b
157
+
158
+ response = agent.input("12+34=?").use_tool(add).get_response()
159
+ full = response.get_data(type="all")
160
+ print(full["extra"]["tool_logs"])
161
+ ```
162
+
163
+ ### 6) 🌐 Unified Model Settings (OpenAICompatible)
164
+ One config for multiple providers and local models.
165
+
166
+ ```python
167
+ from agently import Agently
168
+
169
+ Agently.set_settings(
170
+ "OpenAICompatible",
171
+ {
172
+ "base_url": "https://api.deepseek.com/v1",
173
+ "model": "deepseek-chat",
174
+ "auth": "DEEPSEEK_API_KEY",
175
+ },
176
+ )
177
+ ```
178
+
179
+ ## 🚀 Quickstart
180
+
181
+ ### Install
182
+
183
+ ```bash
184
+ pip install -U agently
185
+ ```
186
+
187
+ *Requirements: Python >= 3.10, recommended Agently >= 4.0.7.2*
188
+
189
+ ### 5‑minute example
190
+
191
+ **1. Structured output**
192
+
193
+ ```python
194
+ from agently import Agently
195
+
196
+ agent = Agently.create_agent()
197
+
198
+ result = (
199
+ agent.input("Introduce Python in one sentence and list 2 advantages")
200
+ .output({
201
+ "introduction": (str, "one sentence"),
202
+ "advantages": [(str, "advantage")]
203
+ })
204
+ .start(ensure_keys=["introduction", "advantages[*]"])
205
+ )
206
+
207
+ print(result)
208
+ ```
209
+
210
+ **2. Workflow routing**
211
+
212
+ ```python
213
+ from agently import TriggerFlow, TriggerFlowEventData
214
+
215
+ flow = TriggerFlow()
216
+
217
+ @flow.chunk
218
+ def classify_intent(data: TriggerFlowEventData):
219
+ text = data.value
220
+ if "price" in text:
221
+ return "price_query"
222
+ if "feature" in text:
223
+ return "feature_query"
224
+ if "buy" in text:
225
+ return "purchase"
226
+ return "other"
227
+
228
+ @flow.chunk
229
+ def handle_price(_: TriggerFlowEventData):
230
+ return {"response": "Pricing depends on the plan..."}
231
+
232
+ @flow.chunk
233
+ def handle_feature(_: TriggerFlowEventData):
234
+ return {"response": "Our product supports..."}
235
+
236
+ (
237
+ flow.to(classify_intent)
238
+ .match()
239
+ .case("price_query")
240
+ .to(handle_price)
241
+ .case("feature_query")
242
+ .to(handle_feature)
243
+ .case_else()
244
+ .to(lambda d: {"response": "What would you like to know?"})
245
+ .end_match()
246
+ .end()
247
+ )
248
+
249
+ print(flow.start("How much does it cost?"))
250
+ ```
251
+
252
+ ## ✅ Is Your App Production‑Ready? — Release Readiness Checklist
253
+
254
+ Based on teams shipping real projects with Agently, this **production readiness checklist** helps reduce common risks before release.
255
+
256
+ | Area | Check | Recommended Practice |
257
+ | :--- | :--- | :--- |
258
+ | **📝 Output Stability** | Are key interfaces stable? | Define schemas with `output()` and lock critical fields with `ensure_keys`. |
259
+ | **⚡ Real‑time UX** | Need updates while generating? | Consume `type="instant"` structured streaming events. |
260
+ | **🔍 Observability** | Tool calls auditable? | Inspect `extra.tool_logs` for full arguments and results. |
261
+ | **🧩 Workflow Robustness** | Complex flows fully tested? | Unit test each TriggerFlow branch and concurrency limit with expected outputs. |
262
+ | **🧠 Memory & Context** | Multi‑turn experience consistent? | Define Session/Memo summary, trimming, and persistence policies. |
263
+ | **📄 Prompt Management** | Can logic evolve safely? | Version and configure prompts to keep changes traceable. |
264
+ | **🌐 Model Strategy** | Can you switch or downgrade models? | Centralize settings with `OpenAICompatible` for fast provider switching. |
265
+ | **🚀 Performance & Scale** | Can it handle concurrency? | Validate async performance in real web‑service scenarios. |
266
+ | **🧪 Quality Assurance** | Regression tests complete? | Create fixed inputs with expected outputs for core scenarios. |
267
+
268
+
269
+ ## 📈 Who Uses Agently to Solve Real Problems?
270
+
271
+ > "Agently helped us turn evaluation rules into executable workflows and keep key scoring accuracy at 75%+, significantly improving bid‑evaluation efficiency." — Project lead at a large energy SOE
272
+
273
+ > "Agently enabled a closed loop from clarification to query planning to rendering, reaching 90%+ first‑response accuracy and stable production performance." — Data lead at a large energy group
274
+
275
+ > "Agently’s orchestration and session capabilities let us ship a teaching assistant for course management and Q&A quickly, with continuous iteration." — Project lead at a university teaching‑assistant initiative
276
+
277
+ **Your project can be next.**
278
+ 📢 [Share your case on GitHub Discussions →](https://github.com/AgentEra/Agently/discussions/categories/show-and-tell)
279
+ ## ❓ FAQ
280
+
281
+ **Q: How is Agently different from LangChain or LlamaIndex?**
282
+ **A:** Agently is **built for production**. It focuses on stable interfaces (contract‑first outputs), readable/testable orchestration (TriggerFlow), and observable tool calls (`tool_logs`). It’s a better fit for teams that need reliability and maintainability after launch.
283
+
284
+ **Q: Which models are supported? Is switching expensive?**
285
+ **A:** With `OpenAICompatible`, you can connect OpenAI, Claude, DeepSeek, Qwen and most OpenAI‑compatible endpoints, plus local models like Llama/Qwen. **The same business code can switch models without rewrites**, reducing vendor lock‑in.
286
+
287
+ **Q: What’s the learning curve? Where should I start?**
288
+ **A:** The core API is straightforward—**you can run your first agent in minutes**. Start with [Quickstart](https://agently.tech/docs/en/quickstart.html), then dive into [Output Control](https://agently.tech/docs/en/output-control/overview.html) and [TriggerFlow](https://agently.tech/docs/en/triggerflow/overview.html).
289
+
290
+ **Q: How do I deploy an Agently‑based service?**
291
+ **A:** Agently doesn’t lock you into a specific deployment path. It provides async APIs and FastAPI examples. The [FastAPI integration example](https://github.com/AgentEra/Agently/tree/main/examples/step_by_step/13-auto_loop_fastapi) covers SSE, WebSocket, and standard POST.
292
+
293
+ **Q: Do you offer enterprise support?**
294
+ **A:** The core framework is open‑source under **Apache 2.0**. For enterprise support, training, or deep collaboration, contact us via the [community](https://doc.weixin.qq.com/forms/AIoA8gcHAFMAScAhgZQABIlW6tV3l7QQf).
295
+
296
+
297
+ ## 🧭 Docs Guide (Key Paths)
298
+
299
+ - **Getting Started**
300
+ - Quickstart: https://agently.tech/docs/en/quickstart.html
301
+ - Model Settings: https://agently.tech/docs/en/model-settings.html
302
+ - Coding Agent Guide: https://agently.tech/docs/en/agent-docs.html
303
+ - **Output Control (Structured Output)**
304
+ - Overview: https://agently.tech/docs/en/output-control/overview.html
305
+ - Output Format: https://agently.tech/docs/en/output-control/format.html
306
+ - ensure_keys: https://agently.tech/docs/en/output-control/ensure-keys.html
307
+ - Instant Streaming: https://agently.tech/docs/en/output-control/instant-streaming.html
308
+ - **Result & Streaming Events**
309
+ - Result Data: https://agently.tech/docs/en/model-response/result-data.html
310
+ - Streaming Events: https://agently.tech/docs/en/model-response/streaming.html
311
+ - **Session & Memo**
312
+ - Overview: https://agently.tech/docs/en/agent-extensions/session-memo/
313
+ - Quickstart: https://agently.tech/docs/en/agent-extensions/session-memo/quickstart.html
314
+ - **TriggerFlow Orchestration**
315
+ - Overview: https://agently.tech/docs/en/triggerflow/overview.html
316
+ - when Branch: https://agently.tech/docs/en/triggerflow/when-branch.html
317
+ - if / elif / else: https://agently.tech/docs/en/triggerflow/if-branch.html
318
+ - match / case: https://agently.tech/docs/en/triggerflow/match-branch.html
319
+ - batch: https://agently.tech/docs/en/triggerflow/batch.html
320
+ - for_each: https://agently.tech/docs/en/triggerflow/for-each.html
321
+ - Runtime Stream: https://agently.tech/docs/en/triggerflow/runtime-stream.html
322
+ - **Tools & Extensions**
323
+ - Tools: https://agently.tech/docs/en/agent-extensions/tools.html
324
+ - MCP: https://agently.tech/docs/en/agent-extensions/mcp.html
325
+ - auto_func: https://agently.tech/docs/en/agent-extensions/auto-func.html
326
+ - KeyWaiter: https://agently.tech/docs/en/agent-extensions/key-waiter.html
327
+ - **Prompt Management**: https://agently.tech/docs/en/prompt-management/overview.html
328
+ - **Async & Settings**: https://agently.tech/docs/en/async-support.html / https://agently.tech/docs/en/settings.html
329
+ - **Playbook**: https://agently.tech/docs/en/agent-systems/overview.html
330
+
331
+ ## 🤝 Community
332
+
333
+ - Discussions: https://github.com/AgentEra/Agently/discussions
334
+ - Issues: https://github.com/AgentEra/Agently/issues
335
+ - WeChat Group: https://doc.weixin.qq.com/forms/AIoA8gcHAFMAScAhgZQABIlW6tV3l7QQf
336
+
337
+ ## 📄 License
338
+
339
+ Agently is licensed under [Apache 2.0](LICENSE).
340
+
341
+ ---
342
+
343
+ <p align="center">
344
+ <b>Start building your production‑ready AI apps →</b><br>
345
+ <code>pip install -U agently</code>
346
+ </p>
347
+
348
+ <p align="center">
349
+ <sub>Questions? Read the <a href="https://agently.tech/docs">docs</a> or join the <a href="https://doc.weixin.qq.com/forms/AIoA8gcHAFMAScAhgZQABIlW6tV3l7QQf">community</a>.</sub>
350
+ </p>
351
+
@@ -6,7 +6,7 @@ agently/builtins/agent_extensions/AutoFuncExtension.py,sha256=TmwMazwPzb5WXfDqfe
6
6
  agently/builtins/agent_extensions/ChatSessionExtension.py,sha256=Bbrc9cn3m-uOY0i8EGq1Cvp71nNpEORrrXTiy_JuCCQ,12048
7
7
  agently/builtins/agent_extensions/ConfigurePromptExtension.py,sha256=9wy2zHIDVHbUlj5sI0A03SscUWSzZNc9hNJSEdXFXd0,11390
8
8
  agently/builtins/agent_extensions/KeyWaiterExtension.py,sha256=Rf8dB8Yt3_9IJifpiE-Rn6lLIXqZjaNp94lnX6Betgw,5555
9
- agently/builtins/agent_extensions/SessionExtension.py,sha256=P4T_13IB_K8zFi_OAEs_pfeyhoBgJ9yuRGeey0hT2Os,11873
9
+ agently/builtins/agent_extensions/SessionExtension.py,sha256=s_pzyLg34D-3rew60gdTxBEiLm1wi0AcoHM7J62-A3U,12162
10
10
  agently/builtins/agent_extensions/ToolExtension.py,sha256=S3jjumHiauEQ-m46Zkh-1I9ih02kKoj8sBEU82woz1E,6886
11
11
  agently/builtins/agent_extensions/__init__.py,sha256=rWD8hzJ0ymxxpjxJCdB0x1L5AnhYQnAJUeIAD_MTQ5I,899
12
12
  agently/builtins/hookers/ConsoleHooker.py,sha256=aJdDj_nG8CiwyelA505zvtpzBSwD52nFIkBRDJGgq3Y,8099
@@ -28,7 +28,7 @@ agently/core/ExtensionHandlers.py,sha256=88iSAW50bgMshB56cTgKg30eOjZQyXiJY1en4w7
28
28
  agently/core/ModelRequest.py,sha256=hroVb-U-knygXMZ1CCy84pJlP1GHSBNZPI809jEgQak,24350
29
29
  agently/core/PluginManager.py,sha256=fwRxvqPMgXYIrclhRHtkaPsyvn6SaeBFqvL7tTzYwck,4410
30
30
  agently/core/Prompt.py,sha256=vCi_64A-ITiJ-6ZL2PduRdTXoHp8xpKJa2z0ejCJ4uQ,6899
31
- agently/core/Session.py,sha256=tWRyjX4cKR_cyK5LaiEqaqkSeyemjzceoaM033mMWMM,3163
31
+ agently/core/Session.py,sha256=YhhwMRfrs6pXKcwQWPcpFCElD2BrSk9JhnZjdP_tUQU,7343
32
32
  agently/core/Tool.py,sha256=PNYf_BwVefr8IOqf5asLaVq2fU7hQaFJwJVj3S4fq84,1871
33
33
  agently/core/TriggerFlow/BluePrint.py,sha256=H_TYymWOci-ZvQzqyRxpkHjO77zlzXLukLUUctX4ftM,4887
34
34
  agently/core/TriggerFlow/Chunk.py,sha256=xPWr_ofpl-iG4jHIJfB5mPanmn70pq7x8GCcz3G8NPc,1583
@@ -40,8 +40,9 @@ agently/core/TriggerFlow/process/BaseProcess.py,sha256=tBAMCI2Yd8Ylfi5CpkhlSVLH5
40
40
  agently/core/TriggerFlow/process/ForEachProcess.py,sha256=DD4frz9mTsKgnPXnHJD53hL6uiiU6h338p7ipud8zMU,4897
41
41
  agently/core/TriggerFlow/process/MatchCaseProcess.py,sha256=MKY5Yh66JiMABhCzamRl8UZOBjbD75TFp84Jw6o_t68,7900
42
42
  agently/core/TriggerFlow/process/__init__.py,sha256=BP5bAr9LRVVD83KFqXeprgTmXA1iCSOSsD509BtoX_E,753
43
- agently/core/__init__.py,sha256=CPglSpW5oEEmWpCpdvv9wK4myXmVipjuZm5HtMq6Vxo,1214
43
+ agently/core/__init__.py,sha256=7QJdqGWgmiGvyXVxvRang9eQkbyNAzJu7RissUtzvcw,1243
44
44
  agently/integrations/chromadb.py,sha256=oGc-eZRK4pXo5QPbjpC1_RoRzhv2Hqadp80TPEEvw8c,10374
45
+ agently/integrations/fastapi.py,sha256=lxOOAcdY36UxuuVsPhvl0MAUjaJT3drv5CubVdqEHyk,668
45
46
  agently/types/__init__.py,sha256=xb8GMY-ULncO_PY9rfRUsyi12wAQQJx8gAAnoM30uZA,592
46
47
  agently/types/data/__init__.py,sha256=ulo2p9SBV-v75zPZWcTczDnAcYO_IlDe3JMGNOZa4vM,1662
47
48
  agently/types/data/event.py,sha256=LFQW7MN_QGOis3XV-8K6jNXWsLvT7tYxo4BZbUBCpfI,1790
@@ -55,7 +56,7 @@ agently/types/plugins/EventHooker.py,sha256=kb80-baVc3fVlrddW5syv9uSD8a2Mcw8Fd3I
55
56
  agently/types/plugins/ModelRequester.py,sha256=urG1zFX0b4U6ZKSO50IbW5IHK3ydmRgUom7O7Niqk8s,3875
56
57
  agently/types/plugins/PromptGenerator.py,sha256=V8kqT0Eeq09AQqfGA-SZ5mNKeit1UrmqlDQCquSMzUU,4752
57
58
  agently/types/plugins/ResponseParser.py,sha256=6dCVWz61gaHOxsX9e5sYFqcWRZ5hBnNXAarT0-9uCUY,4566
58
- agently/types/plugins/Session.py,sha256=SJGBUkUZPTCv7f4Ml3XbhL3A_RgE5no5XT9vHJvMzHY,5189
59
+ agently/types/plugins/Session.py,sha256=WO_V2hW8jWh1bs-cHEIKgLDy6-zj22DjCgFzhdp1VdY,5485
59
60
  agently/types/plugins/ToolManager.py,sha256=q1Y3G_tzh1AU3s13H-zTDZIkR4W1mjh9E6AKudFOvyg,2421
60
61
  agently/types/plugins/__init__.py,sha256=CtSeuBvC9EiS3uX2FWFLLKBkdP6EAdakO-T7LX9TWII,1361
61
62
  agently/types/plugins/base.py,sha256=O3dskXxoc7E1_qd8nTfJr7sP42jbJlPYijXKDX-cM2E,1178
@@ -79,7 +80,7 @@ agently/utils/StreamingJSONCompleter.py,sha256=aZ9zuGUTQlP-QKbXHUZCf6EtVuG49MKn8
79
80
  agently/utils/StreamingJSONParser.py,sha256=sPPJOtj5OYvsrukRErcoxRl4yuV1zDuf7pQ_pvw_Zow,21116
80
81
  agently/utils/TimeInfo.py,sha256=lgdeXpTJQOqw0czvrsMcxNeY84wuvCF7kWpY4W8nhDY,750
81
82
  agently/utils/__init__.py,sha256=A2EXx-TVU3Ca1s3fMlWM0se0BgI-ZRCXZgl4Hue-lco,1352
82
- agently-4.0.7.2.dist-info/METADATA,sha256=QoTPNNNEXhuSgLvqDHL1d9lSE6hjL3dEpEIDH9WeDVY,17085
83
- agently-4.0.7.2.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
84
- agently-4.0.7.2.dist-info/licenses/LICENSE,sha256=Y5ZgAdYgMFigPT8dhN18dTLRtBshOSfWhTDRO1t0Cq4,11360
85
- agently-4.0.7.2.dist-info/RECORD,,
83
+ agently-4.0.7.4.dist-info/METADATA,sha256=gAx8gcMTXLExJBjBRnaILcIezn44bDn9QTn5E_NXJ38,14447
84
+ agently-4.0.7.4.dist-info/WHEEL,sha256=kJCRJT_g0adfAJzTx2GUMmS80rTJIVHRCfG0DQgLq3o,88
85
+ agently-4.0.7.4.dist-info/licenses/LICENSE,sha256=Y5ZgAdYgMFigPT8dhN18dTLRtBshOSfWhTDRO1t0Cq4,11360
86
+ agently-4.0.7.4.dist-info/RECORD,,
@@ -1,433 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: agently
3
- Version: 4.0.7.2
4
- Summary:
5
- License: Apache-2.0
6
- License-File: LICENSE
7
- Author: Agently Team
8
- Author-email: developer@agently.tech
9
- Requires-Python: >=3.10
10
- Classifier: License :: OSI Approved :: Apache Software License
11
- Classifier: Programming Language :: Python :: 3
12
- Classifier: Programming Language :: Python :: 3.10
13
- Classifier: Programming Language :: Python :: 3.11
14
- Classifier: Programming Language :: Python :: 3.12
15
- Classifier: Programming Language :: Python :: 3.13
16
- Classifier: Programming Language :: Python :: 3.14
17
- Requires-Dist: greenlet (>=3.2.3,<4.0.0)
18
- Requires-Dist: httpx (>=0.28.1,<0.29.0)
19
- Requires-Dist: httpx-sse (>=0.4.1,<0.5.0)
20
- Requires-Dist: json5 (>=0.12.0,<0.13.0)
21
- Requires-Dist: packaging (>=25.0,<26.0)
22
- Requires-Dist: pydantic (>=2.11.7,<3.0.0)
23
- Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
24
- Requires-Dist: stamina (>=25.1.0,<26.0.0)
25
- Requires-Dist: toml (>=0.10.2,<0.11.0)
26
- Description-Content-Type: text/markdown
27
-
28
- <img width="640" alt="image" src="https://github.com/user-attachments/assets/c645d031-c8b0-4dba-a515-9d7a4b0a6881" />
29
-
30
- # Agently 4
31
-
32
- [English Introduction](https://github.com/AgentEra/Agently/blob/main/README.md) | [中文介绍](https://github.com/AgentEra/Agently/blob/main/README_CN.md)
33
-
34
-
35
- > *Speed Up Your GenAI Application Development*
36
-
37
- [![license](https://img.shields.io/badge/license-Apache2.0-blue.svg?style=flat-square)](https://github.com/AgentEra/Agently/blob/main/LICENSE)
38
- [![PyPI - Downloads](https://img.shields.io/pypi/dm/agently?style=flat-square)](https://pypistats.org/packages/agently)
39
- [![GitHub star chart](https://img.shields.io/github/stars/agentera/agently?style=flat-square)](https://star-history.com/#agentera/agently)
40
- [![Twitter](https://img.shields.io/twitter/url/https/twitter.com/AgentlyTech.svg?style=social&label=Follow%20%40AgentlyTech)](https://x.com/AgentlyTech)
41
- <a href="https://doc.weixin.qq.com/forms/AIoA8gcHAFMAScAhgZQABIlW6tV3l7QQf">
42
- <img alt="WeChat" src="https://img.shields.io/badge/WeChat%20Group-Apply-brightgreen?logo=wechat&style=flat-square">
43
- </a>
44
-
45
- <p>
46
- <a href="https://github.com/AgentEra/Agently/discussions/categories/general">
47
- <img alt="Discussions" src="https://img.shields.io/badge/Agently%20General%20Discussions-JOIN-brightgreen.svg?style=for-the-badge" />
48
- </a>
49
- <a href="https://github.com/AgentEra/Agently/discussions/categories/contribute-to-agently-4">
50
- <img alt="Contribute" src="https://img.shields.io/badge/Contribute%20to%20Agently%204%20-Join-blueviolet.svg?style=for-the-badge">
51
- </a>
52
- <a href="https://github.com/AgentEra/Agently/issues">
53
- <img alt="Issues" src="https://img.shields.io/badge/Report%20Issues-Report-red.svg?style=for-the-badge">
54
- </a>
55
- </p>
56
-
57
- <hr />
58
-
59
- <p align="center">
60
- <b><a href = "https://Agently.tech">💥 Official WebSite</a> - Everything about Agently and what's coming next</b>
61
- </p>
62
-
63
- <hr />
64
-
65
- ## Getting Started
66
-
67
- Agently is a Python-based framework for building GenAI applications. You can install it via pip and import features using `from agently import Agently`.
68
-
69
- Install the latest version via pip:
70
-
71
- ```shell
72
- pip install -U agently
73
- ```
74
-
75
- > ℹ️ If you're looking for Agently v3's code and documents, please visit branch [`v3-final`](https://github.com/AgentEra/Agently/tree/v3-final)
76
-
77
- Clone the repository and install locally:
78
-
79
- ```shell
80
- git clone git@github.com:AgentEra/Agently.git
81
- cd Agently
82
- pip install -e .
83
- ```
84
-
85
- ## Documentation
86
-
87
- - Docs Site: https://Agently.tech/docs
88
- - Step-by-step tutorials: `examples/step_by_step/`
89
- - Auto Loop FastAPI (SSE/WS/POST, Docker-ready): `examples/step_by_step/13-auto_loop_fastapi/`
90
-
91
- ## What is Agently?
92
-
93
- Agently aims to provide an intuitive, efficient, and developer-friendly framework for GenAI application development. By deeply understanding the runtime control needs of model outputs, Agently bridges the gap between large language models and real-world applications.
94
-
95
- Agently abstracts away the complexities of:
96
- - Varying model parameters
97
- - Output formatting
98
- - Communication between engineering modules and GenAI logic
99
-
100
- ...while giving developers full control over business logic and integration with existing systems.
101
-
102
- We believe GenAI is not a generational replacement for current systems but a powerful extension. Engineers and tools are key to turning GenAI's possibilities into reality.
103
-
104
- Our mission is to build the best developer experience (DX) for GenAI application engineers.
105
-
106
- ## From Demo to Production
107
-
108
- In real teams, the hardest part is rarely “can the model answer?”—it’s whether the system can survive real traffic, real data, and real dependencies while staying testable, observable, and maintainable. Agently is built to pull LLM uncertainty back inside an engineering boundary.
109
-
110
- - **Contract-first structured outputs (framework-native, provider-agnostic)**: define schemas with `output()`, enforce critical paths with `ensure_keys`, and parse/repair in the framework pipeline (no hard dependency on provider-specific `response_format` / JSON-schema switches). This keeps interfaces stable even when you switch models or inference servers.
111
- - **Tool planning + traceability without vendor lock-in**: deciding whether to use a tool, selecting a tool, and building kwargs is a built-in planning step in the framework, not something that requires function-calling support. Every run leaves evidence in `extra` (`tool_logs` / tool calls) for debugging and audit.
112
- - **Workflow orchestration you can maintain**: TriggerFlow translates visual “low-code graphs” (n8n/Dify/Coze style) into readable code with events, branching, joins, loops, and concurrency limits. Combined with Instant-mode partial node capture + signal-driven execution, you can do real-time UX like “companion robot speaks while actions trigger”.
113
- - **Grounded answers with citations**: KB retrieval results are structured (`id/document/metadata`) and can be turned into enforced citations (e.g. `source_id` + `quote`) so answers are traceable and reviewable.
114
-
115
- ## Core Features Overview
116
-
117
- These are the production pain points we keep seeing across teams:
118
- - **“I asked for JSON, got a paragraph.”** Missing keys, format drift, extra prose → broken parsers.
119
- - **Tools that work… until they don’t.** Failures become hard to reproduce, debug, and audit.
120
- - **Low-code graphs that outgrow themselves.** More branches, more state, less confidence to change.
121
- - **RAG without accountability.** You can’t answer: “Which doc supports this claim?”
122
-
123
- Agently turns them into engineering primitives you can ship with confidence: schema-first outputs (`output()` + `ensure_keys`), Instant-mode structured streaming, framework-native tool planning with traces, TriggerFlow orchestration, and KB grounding with citations.
124
-
125
- ### Structured and Streamed Output Control for LLMs
126
-
127
- Schema-first outputs are often the difference between a demo and an API: you define what the system must return, and the framework enforces it at runtime.
128
-
129
- Agently allows you to control and consume model outputs using a developer-centric pattern:
130
-
131
- ```python
132
- from agently import Agently
133
-
134
- agent = Agently.create_agent()
135
-
136
- (
137
- agent
138
- .input("What time is it now?", always=True)
139
- .info({
140
- "default_timezone": "",
141
- "tool_list": [{
142
- "name": "get_current_time",
143
- "desc": "Get current time by time zone provided",
144
- "kwargs": {
145
- "timezone_str": (str, "time zone string in ZoneInfo()"),
146
- },
147
- }]
148
- })
149
- .output({
150
- "first_time_response": (str, ),
151
- "tool_using_judgement": (bool, ),
152
- "tool_using_command": (
153
- {
154
- "name": (str, "Decide which tool to use by tool name:{tool_list.[].name}"),
155
- "kwargs": (dict, "According {tool_list.[].args} to output kwargs dictionary"),
156
- },
157
- "If {tool_using_judgement}==False, just output {}",
158
- ),
159
- })
160
- )
161
- ```
162
-
163
- Then, consume the model response:
164
-
165
- ```python
166
- response = agent.get_response()
167
-
168
- # Get raw text
169
- response_text = response.get_text()
170
-
171
- # Get parsed structured data
172
- response_data = response.get_data()
173
-
174
- # Instant parsing mode (structured streaming)
175
- instant_response_generator = response.get_generator(type="instant")
176
-
177
- use_tool = False
178
-
179
- for instant_message in instant_response_generator:
180
- if instant_message.path == "first_time_response":
181
- if instant_message.delta is not None:
182
- print(instant_message.delta, end="", flush=True)
183
- elif instant_message.path == "tool_using_judgement":
184
- use_tool = instant_message.value
185
- print()
186
- if use_tool:
187
- print("[USE TOOL!]")
188
- else:
189
- print("[NO NEED TO USE TOOL!]")
190
- if use_tool:
191
- if instant_message.path == "tool_using_command.name" and instant_message.is_complete:
192
- print(f"I want to use: '{ instant_message.value }'")
193
- elif instant_message.path == "tool_using_command":
194
- print(f"call: { instant_message.value }")
195
- print(f"kwargs: { instant_message.value }")
196
- ```
197
-
198
- ```shell
199
- I can check the current time for you. Please specify a timezone (e.g., 'America/New_York') so I can provide the accurate time.
200
- [NO NEED TO USE TOOL!]
201
- ```
202
-
203
- ### Provider Compatibility (Local / Hosted / Proxy)
204
-
205
- Agently unifies model configuration via `OpenAICompatible`, so you can switch between providers while keeping the same developer experience. It also supports common “production reality” knobs like `full_url` and custom auth headers.
206
-
207
- - Minimal example:
208
- ```python
209
- from agently import Agently
210
-
211
- Agently.set_settings(
212
- "OpenAICompatible",
213
- {
214
- "base_url": "https://api.deepseek.com/v1",
215
- "model": "deepseek-chat",
216
- "auth": "DEEPSEEK_API_KEY",
217
- },
218
- )
219
- ```
220
-
221
- - Example configs: `examples/model_configures/`
222
- - Step-by-step: `examples/step_by_step/01-settings.py`
223
-
224
- ### Output Reliability (ensure_keys + retries)
225
-
226
- In batch tasks and pipelines, a missing field can crash the whole job. Agently provides `ensure_keys` + retries for structured output so you can enforce required fields (including wildcard paths for list items).
227
-
228
- - Minimal example:
229
- ```python
230
- from agently import Agently
231
-
232
- agent = Agently.create_agent()
233
- result = (
234
- agent.input("Give me 3 todos")
235
- .output({"todos": [("str", "todo item")]})
236
- .start(ensure_keys=["todos[*]"], max_retries=2, raise_ensure_failure=False)
237
- )
238
- print(result)
239
- ```
240
- ```text
241
- Output (qwen2.5:7b):
242
- {'todos': ['Schedule morning exercise routine', 'Prepare presentation slides for the meeting', 'Respond to emails from clients']}
243
- ```
244
-
245
- - Step-by-step: `examples/step_by_step/03-output_format_control.py`
246
-
247
- ### Streaming UX (delta / instant / typed_delta)
248
-
249
- Agently streaming is designed for real applications: reduce waiting, expose decisions early, and route structured fields to different UI regions. For example in a “companion robot” HCI scenario, you often want to mix user-facing text with machine/behavior commands, and consume them as soon as they are parsed.
250
-
251
- - Minimal example:
252
- ```python
253
- from agently import Agently
254
-
255
- agent = Agently.create_agent()
256
- response = (
257
- agent.input("Act as a companion robot: greet me and propose a small action you can do next.")
258
- .output(
259
- {
260
- "thinking": ("str", "internal planning (not for users)"),
261
- "say": ("str", "what the user sees/hears"),
262
- "actions": [("str", "robot action command(s) for your app to execute")],
263
- }
264
- )
265
- .get_response()
266
- )
267
-
268
- say_label_printed = False
269
-
270
- def execute_action(action: str) -> None:
271
- # In real apps, route this to your robot controller / UI event bus.
272
- print(f"\n[action] {action}")
273
-
274
- for msg in response.get_generator(type="instant"):
275
- if msg.path == "say" and msg.delta:
276
- if not say_label_printed:
277
- print("[say] ", end="")
278
- say_label_printed = True
279
- print(msg.delta, end="", flush=True)
280
- if msg.path.startswith("actions[") and msg.is_complete:
281
- execute_action(msg.value)
282
- print()
283
- ```
284
- ```text
285
- Output (qwen2.5:7b):
286
- [say] Hello! Nice to meet you. How about we start with some light conversation? Do you have any favorite hobbies or interests that we could talk about?
287
- [action] initiate_conversation
288
- ```
289
-
290
- - Step-by-step: `examples/step_by_step/06-streaming.py`
291
- - Reference patterns: `examples/basic/streaming_print.py`
292
-
293
- ### Tools (built-in + custom + traceable)
294
-
295
- When a project grows from 1 tool to 20 tools, “it worked yesterday” isn’t enough—you need predictable planning and a trail you can audit.
296
-
297
- Tools let the model call external functions deterministically. Agently supports:
298
- - built-in `Search` / `Browse`
299
- - custom tools via decorator
300
- - tool call tracing from response metadata (`extra`)
301
-
302
- Unlike workflows that rely on provider-side function calling, Agently can run a framework-native “tool planning” step even on plain chat endpoints, so tool orchestration stays portable across most modern models.
303
-
304
- - Minimal example:
305
- ```python
306
- from agently import Agently
307
-
308
- agent = Agently.create_agent()
309
-
310
- @agent.tool_func
311
- def add(*, a: int, b: int) -> int:
312
- return a + b
313
-
314
- agent.use_tools(add)
315
- print(agent.input("Use the add tool to calculate 12 + 34.").start())
316
- ```
317
- ```text
318
- Output (qwen2.5:7b):
319
- The sum of 12 and 34 is calculated as follows:
320
-
321
- 12
322
- +34
323
- -----
324
- 46
325
-
326
- Therefore, the result of 12 + 34 is **46**.
327
-
328
- No external sources were used in this calculation.
329
- ```
330
-
331
- - Step-by-step: `examples/step_by_step/07-tools.py`
332
-
333
- ### Workflow Orchestration (TriggerFlow)
334
-
335
- TriggerFlow is for the moment your workflow stops being a sketch: you need events, joins, loops, concurrency limits, and long-term maintainability (including migrating from n8n/Dify/Coze-style graphs into code).
336
-
337
- TriggerFlow is Agently’s event-driven workflow engine, designed for:
338
- - branching (`when`, `if_condition`, `match`)
339
- - concurrency limits (`batch`, `for_each`)
340
- - loops (`emit` + `when`)
341
- - runtime stream events (`put_into_stream`)
342
-
343
- - Minimal example:
344
- ```python
345
- from agently import TriggerFlow
346
-
347
- flow = TriggerFlow()
348
- flow.to(lambda d: f"Hello, {d.value}").end()
349
- print(flow.start("Agently"))
350
- ```
351
- ```text
352
- Output (qwen2.5:7b):
353
- Hello, Agently
354
- ```
355
-
356
- - TriggerFlow series: `examples/step_by_step/11-triggerflow-01_basics.py`
357
-
358
- ### Knowledge Base (embeddings + vector DB)
359
-
360
- In enterprise RAG, the question is rarely “can we retrieve?”—it’s “can we cite and defend the answer?”.
361
-
362
- Agently integrates KB pipelines (e.g., Chroma) to ground responses with real documents and metadata.
363
-
364
- - Minimal example:
365
- ```python
366
- from agently import Agently
367
- from agently.integrations.chromadb import ChromaCollection
368
-
369
- embedding = Agently.create_agent()
370
- embedding.set_settings(
371
- "OpenAICompatible",
372
- {
373
- "model": "qwen3-embedding:0.6b",
374
- "base_url": "http://127.0.0.1:11434/v1/",
375
- "auth": "nothing",
376
- "model_type": "embeddings",
377
- },
378
- )
379
- kb = ChromaCollection(collection_name="demo", embedding_agent=embedding)
380
- kb.add([{"document": "Agently is a GenAI framework.", "metadata": {"source": "demo"}}])
381
- print(kb.query("What is Agently?"))
382
- ```
383
-
384
- - Step-by-step: `examples/step_by_step/09-knowledge_base.py`
385
-
386
- ### Deployment Templates (FastAPI, Docker)
387
-
388
- For engineering delivery, the repo includes a docker-ready FastAPI project that exposes Auto Loop through:
389
- - SSE streaming
390
- - WebSocket
391
- - POST
392
-
393
- - Minimal example:
394
- ```shell
395
- cd examples/step_by_step/13-auto_loop_fastapi
396
- uvicorn app.main:app --reload
397
- ```
398
-
399
- - Project: `examples/step_by_step/13-auto_loop_fastapi/`
400
-
401
- ### Learn by Examples (Recommended Path)
402
-
403
- Start with these step-by-step chapters (runnable code + explanations in docs):
404
- - Settings → `examples/step_by_step/01-settings.py`
405
- - Prompt Methods → `examples/step_by_step/02-prompt_methods.py`
406
- - Output Control → `examples/step_by_step/03-output_format_control.py`
407
- - Streaming → `examples/step_by_step/06-streaming.py`
408
- - Tools → `examples/step_by_step/07-tools.py`
409
- - TriggerFlow → `examples/step_by_step/11-triggerflow-01_basics.py`
410
- - Auto Loop → `examples/step_by_step/12-auto_loop.py`
411
-
412
- ## Agently Helper (Desktop)
413
-
414
- Agently Helper is a desktop tool to help you quickly **understand** and **test** Agently capabilities without setting up a full project first:
415
- - Multi-model management and switching
416
- - Switching between different prompt styles
417
- - Structured output
418
- - Streaming output
419
-
420
- - Windows: https://1drv.ms/u/c/13d5207d1b13e4d3/IQC9XITZl83hR5vU9Z_t-0oKAd3jtMh_fYRypp7T2k8JhCY?e=I72GVH
421
- - macOS (Apple Silicon): https://1drv.ms/u/c/13d5207d1b13e4d3/IQBhdxYw9Ev1R6qTWb-esVK2AY8PwCxnBHLNuf06Ic4w7sw?e=unMjaD
422
- - macOS (Intel): https://1drv.ms/u/c/13d5207d1b13e4d3/IQDqUPSqRq7LR7gpCjK60FOSASl4PBsRZPGtHvBAA63U_js?e=EmwVMA
423
- - Linux: https://1drv.ms/u/c/13d5207d1b13e4d3/IQDVenHvItjFTqnlv294MPD9AUQDvkAKwvBcNufEXSl1nAs?e=Ti5aJ7
424
-
425
- ## 💬 WeChat Group (Join Us)
426
-
427
- > [Click Here to Apply](https://doc.weixin.qq.com/forms/AIoA8gcHAFMAScAhgZQABIlW6tV3l7QQf)
428
- > or Scan the QR Code Below:
429
-
430
- <p align="center">
431
- <img width="120" alt="WeChat QR" src="https://github.com/AgentEra/Agently/assets/4413155/7f4bc9bf-a125-4a1e-a0a4-0170b718c1a6">
432
- </p>
433
-